From 8308de09818d77e7367f67ddbf87aff7af873313 Mon Sep 17 00:00:00 2001 From: Sergey <93754860+slaid098@users.noreply.github.com> Date: Thu, 30 Jul 2026 03:07:09 +0300 Subject: [PATCH] feat(create-readme): add clickable steps to Quick Start (#146) * feat(create-readme): add clickable steps and conditional bash block * docs(repo-readme): document clickable steps params and example * docs(handoff): add handoff and ADR for clickable-steps * docs(handoff): set PR number * docs(project-map): update create-readme params after PR#146 --------- Co-authored-by: opencode-agent --- .opencode/skills/repo-readme/SKILL.md | 68 +++++++++++++++-- .opencode/tools/create-readme.ts | 42 +++++++++-- docs/decisions/062-pr-146-clickable-steps.md | 57 ++++++++++++++ docs/handoff/pr-146-clickable-steps.md | 78 ++++++++++++++++++++ docs/project-map/README.md | 4 +- 5 files changed, 234 insertions(+), 15 deletions(-) create mode 100644 docs/decisions/062-pr-146-clickable-steps.md create mode 100644 docs/handoff/pr-146-clickable-steps.md diff --git a/.opencode/skills/repo-readme/SKILL.md b/.opencode/skills/repo-readme/SKILL.md index 702d410..0159a63 100644 --- a/.opencode/skills/repo-readme/SKILL.md +++ b/.opencode/skills/repo-readme/SKILL.md @@ -104,7 +104,8 @@ repos/{owner}/{repo}/contents/README.md` с base64-контентом и SHA. {git clone строка, если include_clone !== false} {quick_start} \`\`\` -{access_url строка если передан — Access at {url}} +{quick_start_steps_en — нумерованный список кликабельных шагов, если передан: 1. ... 2. ...} +{access_url строка если передан — Access at [url](url), кликабельна} {development_en блок, если передан — ### 🔧 Development + content} --- @@ -134,7 +135,8 @@ repos/{owner}/{repo}/contents/README.md` с base64-контентом и SHA. {git clone строка, если include_clone !== false} {quick_start} \`\`\` -{access_url строка если передан — Доступ: {url}} +{quick_start_steps_ru — нумерованный список кликабельных шагов, если передан: 1. ... 2. ...} +{access_url строка если передан — Доступ: [url](url), кликабельна} {development_ru блок, если передан — ### 🔧 Разработка + content} --- @@ -148,7 +150,8 @@ repos/{owner}/{repo}/contents/README.md` с base64-контентом и SHA. features), непустой контент между ними, ссылку `slaid098.dev/support`, секции Quick Start (EN) и Быстрый старт (RU), language switcher `[English]` / `[Русский]`, заголовок `## 🇷🇺 Русский` (не "Русская версия"), anchor -`[Русский](#-русский)` (не `#-русская-версия`). +`[Русский](#-русский)` (не `#-русская-версия`). Шаги `quick_start_steps_*` +не влияют на валидацию — они рендерятся вне delimiter-пар (summary/features). ## 6. Независимость от repo-init @@ -171,8 +174,15 @@ Quick Start (EN) и Быстрый старт (RU), language switcher `[English] `{ title, content }` (optional). - `access_url` — URL для Access/Доступ строки после Quick Start bash-блока (optional). EN: `Access at {url}`, RU: `Доступ: {url}`. Omit if no web access. + Рендерится как markdown-ссылка `[url](url)` — кликабельна на GitHub. - `include_clone` — boolean (optional, default true). `false` убирает `git clone` из Quick Start. Для userscript, web-app, npm-package. +- `quick_start_steps_en` / `quick_start_steps_ru` — массивы raw-markdown строк + (optional). Каждая строка = один шаг, может содержать markdown-ссылки + `[text](url)`. Рендерятся как нумерованный список `1. ... 2. ...` ПОСЛЕ + bash-блока, ДО `access_url`. Кликабельные шаги для setup, где установка — это + не одна команда, а несколько ссылок (установить userscript, получить API-ключ, + настроить). Если массив пуст/не передан — шаги не рендерятся (backward compat). - `development_en` — raw markdown (optional). `### 🔧 Development` после EN Quick Start, вне delimiter-тегов (не на slaid098.dev). - `development_ru` — raw markdown (optional). `### 🔧 Разработка` после RU @@ -186,8 +196,56 @@ Quick Start (EN) и Быстрый старт (RU), language switcher `[English] - `include_clone: false` — убирает `git clone` из Quick Start - `quick_start` — команда установки (npm install, pip install, или ссылка на - установку userscript) -- `access_url` — URL web-доступа (если есть) + установку userscript). Если установка — это несколько ссылок (а не одна + команда), лучше использовать `quick_start_steps_*` вместо/вместе с bash-блоком. +- `quick_start_steps_en` / `quick_start_steps_ru` — кликабельные шаги setup + (рекомендуется для userscript/multi-step setup): установить userscript, + получить API-ключ, настроить. Каждая строка может содержать `[text](url)`. + Рендерятся как нумерованный список ПОСЛЕ bash-блока. +- `access_url` — URL web-доступа (если есть), рендерится как `[url](url)`. - `development_en` / `development_ru` — инструкции для разработчиков (как собрать, как контрибьютить), рендерятся после Quick Start, вне delimiter-тегов (не на slaid098.dev) + +### Пример: userscript со шагами-ссылками + +```json +{ + "mode": "create", + "repo_name": "my-userscript", + "tagline": "One-line tagline.", + "why_en": "Why this exists.", + "what_en": "What it does.", + "why_ru": "Зачем этот проект.", + "what_ru": "Что делает.", + "quick_start": "", + "include_clone": false, + "features_en": [{ "emoji": "⚡", "name": "Fast", "description": "Instant setup" }], + "features_ru": [{ "emoji": "⚡", "name": "Быстрый", "description": "Мгновенный старт" }], + "access_url": "http://localhost:4096", + "quick_start_steps_en": [ + "Install the [userscript](https://greasyfork.org/...)", + "Get a [Groq API key](https://console.groq.com/keys)", + "Configure [settings](https://example.com/settings)" + ], + "quick_start_steps_ru": [ + "Установи [юзерскрипт](https://greasyfork.org/...)", + "Получи [ключ Groq](https://console.groq.com/keys)", + "Настрой [параметры](https://example.com/settings)" + ] +} +``` + +Результат (EN секция): + +```markdown +### ⚡ Quick Start +1. Install the [userscript](https://greasyfork.org/...) +2. Get a [Groq API key](https://console.groq.com/keys) +3. Configure [settings](https://example.com/settings) + +Access at [http://localhost:4096](http://localhost:4096) +``` + +`include_clone: false` + `quick_start: ""` → bash-блок не рендерится (только +шаги). Если `quick_start` непустой — bash-блок рендерится перед шагами. diff --git a/.opencode/tools/create-readme.ts b/.opencode/tools/create-readme.ts index 8590e64..9b9e7e7 100644 --- a/.opencode/tools/create-readme.ts +++ b/.opencode/tools/create-readme.ts @@ -22,6 +22,8 @@ type CreateArgs = { development_ru?: string custom_sections_en?: CustomSection[] custom_sections_ru?: CustomSection[] + quick_start_steps_en?: string[] + quick_start_steps_ru?: string[] } function extractBetween(text: string, start: string, end: string): string | null { @@ -41,6 +43,12 @@ function renderFeaturesTable(features: Feature[], isRu: boolean): string { return header + rows } +function renderSteps(steps?: string[]): string { + if (!steps || steps.length === 0) return "" + const items = steps.map((s, i) => `${i + 1}. ${s}`).join("\n") + return `\n${items}\n` +} + function generateReadme(args: CreateArgs): string { const customEn = (args.custom_sections_en || []) .map((s) => `\n\n### ${s.title}\n${s.content}`) @@ -48,14 +56,26 @@ function generateReadme(args: CreateArgs): string { const customRu = (args.custom_sections_ru || []) .map((s) => `\n\n### ${s.title}\n${s.content}`) .join("") - const accessLineEn = args.access_url ? `\nAccess at ${args.access_url}\n` : "" - const accessLineRu = args.access_url ? `\nДоступ: ${args.access_url}\n` : "" + const accessLineEn = args.access_url ? `\nAccess at [${args.access_url}](${args.access_url})\n` : "" + const accessLineRu = args.access_url ? `\nДоступ: [${args.access_url}](${args.access_url})\n` : "" const developmentBlockEn = args.development_en ? `\n\n### 🔧 Development\n${args.development_en}\n` : "" const developmentBlockRu = args.development_ru ? `\n\n### 🔧 Разработка\n${args.development_ru}\n` : "" + const cloneLine = args.include_clone !== false + ? `git clone https://github.com/slaid098/${args.repo_name}.git\n` + : "" + const hasBashBlock = args.include_clone !== false || args.quick_start !== "" + const bashBlockEn = hasBashBlock + ? `\`\`\`bash\n${cloneLine}${args.quick_start}\n\`\`\`` + : "" + const bashBlockRu = hasBashBlock + ? `\`\`\`bash\n${cloneLine}${args.quick_start}\n\`\`\`` + : "" + const stepsEn = renderSteps(args.quick_start_steps_en) + const stepsRu = renderSteps(args.quick_start_steps_ru) return `# 🚀 ${args.repo_name} > ${args.tagline} @@ -79,9 +99,7 @@ ${renderFeaturesTable(args.features_en, false)} ${customEn} ### ⚡ Quick Start -\`\`\`bash -${args.include_clone !== false ? `git clone https://github.com/slaid098/${args.repo_name}.git\n` : ""}${args.quick_start} -\`\`\`${accessLineEn}${developmentBlockEn} +${bashBlockEn}${stepsEn}${accessLineEn}${developmentBlockEn} --- ## 🇷🇺 Русский @@ -99,9 +117,7 @@ ${renderFeaturesTable(args.features_ru, true)} ${customRu} ### ⚡ Быстрый старт -\`\`\`bash -${args.include_clone !== false ? `git clone https://github.com/slaid098/${args.repo_name}.git\n` : ""}${args.quick_start} -\`\`\`${accessLineRu}${developmentBlockRu} +${bashBlockRu}${stepsRu}${accessLineRu}${developmentBlockRu} --- ## 💬 Support and contacts / Поддержка и контакты @@ -195,6 +211,14 @@ export default tool({ .string() .optional() .describe("Install/setup command (e.g. 'pip install -r requirements.txt'). Required for create mode."), + quick_start_steps_en: tool.schema + .array(tool.schema.string()) + .optional() + .describe("Array of raw-markdown strings rendered as a numbered list of clickable steps after the EN Quick Start bash block. Each string = one step, may contain markdown links [text](url). Omit to render only the bash block."), + quick_start_steps_ru: tool.schema + .array(tool.schema.string()) + .optional() + .describe("Array of raw-markdown strings rendered as a numbered list of clickable steps after the RU Быстрый старт bash block. Each string = one step, may contain markdown links [text](url). Omit to render only the bash block."), features_en: tool.schema .array( tool.schema.object({ @@ -296,6 +320,8 @@ export default tool({ development_ru: args.development_ru, custom_sections_en: args.custom_sections_en, custom_sections_ru: args.custom_sections_ru, + quick_start_steps_en: args.quick_start_steps_en, + quick_start_steps_ru: args.quick_start_steps_ru, }) if (args.repo) { diff --git a/docs/decisions/062-pr-146-clickable-steps.md b/docs/decisions/062-pr-146-clickable-steps.md new file mode 100644 index 0000000..de6158c --- /dev/null +++ b/docs/decisions/062-pr-146-clickable-steps.md @@ -0,0 +1,57 @@ +# ADR-062: Clickable steps in Quick Start + conditional bash block (PR-146) + +## Статус +Accepted (2026-07-29) + +## Контекст +Тулза `create-readme` генерировала Quick Start как единый bash code-fence +(``` ```bash ... ``` ```) с опциональной плоской строкой `Access at {url}` / +`Доступ: {url}` после него. Два ограничения: + +1. **Markdown-ссылки внутри code-fence не кликабельны** — для multi-step setup + (userscript: установить скрипт → получить API-ключ → настроить) bash-блок + не подходит: нужны кликабельные `[text](url)` шаги. +2. **Пустой bash-блок портил README** — при `quick_start` пустом и + `include_clone=false` рендерился пустой ` ```bash ``` ` (визуальный мусор). +3. **`access_url` был bare URL** — авто-линк GitHub делает его кликабельным, + но явная markdown-ссылка `[url](url)` надёжнее и в raw-просмотре. + +## Решение +1. **Новые параметры** `quick_start_steps_en?: string[]` / + `quick_start_steps_ru?: string[]` — массивы raw-markdown строк. Каждый + элемент = один шаг, может содержать `[text](url)`. +2. **Helper `renderSteps(steps?)`** — возвращает `""` для пустого/undefined + массива, иначе нумерованный список `\n1. ...\n2. ...\n` с пустыми строками + вокруг (markdown-разделение от code-fence). +3. **Рендер шагов** — `${renderSteps(...)}` вставлен ПОСЛЕ bash-блока, ДО + `accessLine*`, в обеих секциях (EN и RU). +4. **Условный bash-блок** — `hasBashBlock = include_clone !== false || + quick_start !== ""`. Если оба false → bash-fence не рендерится (только + шаги, если есть). `cloneLine` вынесен в общую переменную. Default behavior + сохранён (`include_clone` не передан → `!== false` → true → git clone). +5. **Кликабельный `access_url`** — `[url](url)` вместо bare URL. +6. **Валидатор** — без изменений: шаги вне delimiter-пар, секции "Quick Start" / + "Быстрый старт" присутствуют всегда (заголовок рендерится независимо). + +Backward compatibility: все существующие параметры/вызовы работают как раньше. +Новые параметры опциональны — если не переданы, Quick Start выглядит как +раньше (bash-блок + опциональный clickable `access_url`). + +## Альтернативы +- **Шаги внутри code-fence** — отвергнуто: markdown-ссылки `[text](url)` НЕ + кликабельны внутри ``` ```bash ``` (рендерятся как текст). Нужен + нумерованный markdown-список вне code-fence. +- **Отдельный параметр `steps_mode: "bash" | "list"`** — отвергнуто: bash-блок + и steps не взаимоисключающи. Можно рендерить bash-команду (установка) И шаги + (получить ключ, настроить) вместе. Условный bash-блок через `hasBashBlock` + покрывает все кейсы. +- **Ослабить required-чек `quick_start` в `execute()`** — отвергнуто: спека + не просит менять required-валидацию. Сценарий "только шаги без bash" через + `execute` требует `quick_start: ""` + `include_clone: false`, но `execute` + отвергает пустой `quick_start`. `generateReadme` обрабатывает `quick_start="" + ` корректно (bash не рендерится). Ослабление required — отдельное решение, + вне scope этого PR. +- **Всегда рендерить bash-блок** — отвергнуто: пустой ` ```bash ``` ` портит + README (исходная проблема). Условный рендер через `hasBashBlock` чище. +- **`access_url` как inline `[text](url)` с настраиваемым текстом** — отвергнуто: + спека явно требует `[url](url)` (URL как text). Простой и предсказуемый. \ No newline at end of file diff --git a/docs/handoff/pr-146-clickable-steps.md b/docs/handoff/pr-146-clickable-steps.md new file mode 100644 index 0000000..f483496 --- /dev/null +++ b/docs/handoff/pr-146-clickable-steps.md @@ -0,0 +1,78 @@ +--- +pr: 146 +title: "feat(create-readme): add clickable steps to Quick Start" +--- + +## Что сделано +Расширение тулзы `create-readme` (`.opencode/tools/create-readme.ts`) — +кликабельные шаги-ссылки в Quick Start, условный bash-блок, кликабельный +`access_url`. Backward-compatible. + +1. **Новые параметры** — `quick_start_steps_en?: string[]` и + `quick_start_steps_ru?: string[]` добавлены в тип `CreateArgs` (строки 25-26) + и в `tool.schema` (после `quick_start`, строки 214-220). Каждый элемент = + raw-markdown строка, может содержать `[text](url)`. `.optional()`. +2. **Helper `renderSteps`** (строки 46-50) — если массив пуст/undefined → `""`, + иначе нумерованный список `1. ...\n2. ...\n` с пустыми строками вокруг для + markdown-разделения от code-fence. +3. **Рендер шагов** — `${renderSteps(args.quick_start_steps_*)}` вставлен после + bash-блока, до `accessLine*` в обеих секциях (EN: строка 102, RU: строка 120). +4. **Условный bash-блок** — `hasBashBlock = include_clone !== false || + quick_start !== ""` (строка 70). Если оба условия false → bash-fence НЕ + рендерится (только шаги). `cloneLine` вынесен в общую переменную (строки + 67-69). Применено к EN (`bashBlockEn`, строки 71-73) и RU (`bashBlockRu`, + строки 74-76). Default behavior сохранён: `include_clone` не передан → + `!== false` → true → git clone рендерится. +5. **Кликабельный `access_url`** (строки 59-60) — bare URL заменён на + `[url](url)`. EN: `Access at [url](url)`, RU: `Доступ: [url](url)`. +6. **Передача параметров** — `quick_start_steps_en/ru` прокинуты в вызов + `generateReadme({...})` в `execute()` (строки 324-325). +7. **Валидатор** — `validateReadme` НЕ изменён: шаги рендерятся вне delimiter-пар + (summary/features) и не влияют ни на одну проверку (секции "Quick Start" / + "Быстрый старт" присутствуют). Проверено smoke-тестом: все 28 checks PASS, + `validate` проходит на всех сгенерированных README. +8. **Документация** — `.opencode/skills/repo-readme/SKILL.md` обновлён: новые + параметры в секции 7, кликабельный `access_url` отмечен, reference-шаблон + (секция 5) отражает шаги + clickable access, секция 8 (кейс userscript) + расширена, добавлен пример вызова + ожидаемый вывод. + +### Smoke-test +Прогнан через `npx tsx` с реальным `@opencode-ai/plugin` из +`.opencode/node_modules/` (mock не понадобился — плагин установлен). 5 +сценариев, 28 assertions: +- S1: default (backward compat) — bash + git clone, no steps, no access. +- S2: steps + clickable access (main spec scenario) — вывод точно совпадает с + примером из issue. +- S3: asymmetric (steps only on RU). +- S4: `include_clone=false` + non-empty `quick_start` → bash без git clone. +- S5: empty steps array → no list rendered. +Все `validate` PASS на сгенерированных README. + +## Почему +Quick Start умел только bash-блок (markdown-ссылки внутри code-fence НЕ +кликабельны) и плоский `access_url`. Для multi-step setup (userscript: +установить → получить API-ключ → настроить) нужен нумерованный список +кликабельных шагов. Условный bash-блок убирает пустой ` ```bash ``` ` когда +установки нет (только шаги). Кликабельный `access_url` улучшает UX. + +## Pending +— + +## Watch out +- **`quick_start` остаётся required** для `create` mode в `execute()` (строки + 296-301). Сценарий "пустой quick_start + только steps" через `execute` + невозможен без ослабления required-чеков — спека этого НЕ просит. Чтобы + рендерить только шаги (без bash), передай `quick_start: ""` + `include_clone: + false` — НО `execute` отвергнет пустой `quick_start`. Обход: передать + минимальный `quick_start` (например пробел/коммент) или ослабить required в + отдельном issue. В `generateReadme` напрямую логика `hasBashBlock` корректна + для `quick_start=""`. +- **`access_url` визуально почти идентичен** на GitHub: был bare URL (auto-link) + → стал `[url](url)` (markdown-ссылка). Оба кликабельны. На raw-text + просмотрах (не GitHub) markdown-синтаксис виден. +- **tsconfig.smoke.json и smoke-harness** созданы в `/tmp/opencode/` (вне + репо) — не коммитятся. Mock `@opencode-ai/plugin` не понадобился (реальный + плагин установлен в `.opencode/node_modules/`, gitignored). +- **`renderSteps` использует `1-based` нумерацию** через `${i + 1}.` — markdown + ordered list, нумерация авто-пересчитывается рендерером, но явные числа + соответствуют порядку массива. \ No newline at end of file diff --git a/docs/project-map/README.md b/docs/project-map/README.md index 097c960..f67b90b 100644 --- a/docs/project-map/README.md +++ b/docs/project-map/README.md @@ -36,7 +36,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) — PR#112, PR#116, PR#118, PR#130 +│ │ ├── 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 │ │ ├── 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 +45,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 optional params, RU heading 'Русский' + anchor checks, 4 delimiter pairs for slaid098.dev; local fs + remote gh api) — PR#112, PR#116, PR#118, PR#130 +│ │ ├── 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 │ │ ├── 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