fix(create-readme): allow empty quick_start with steps-only Quick Start (#149)
* fix(create-readme): allow empty quick_start with steps-only Quick Start * docs(handoff): add handoff + ADR for empty-quick-start * docs(handoff): set PR number * docs(project-map): update create-readme.ts role for PR#149 --------- Co-authored-by: opencode-agent <agent@opencode.local>
This commit is contained in:
parent
8308de0981
commit
4bb504a604
4 changed files with 102 additions and 8 deletions
|
|
@ -13,7 +13,7 @@ type CreateArgs = {
|
||||||
what_en: string
|
what_en: string
|
||||||
why_ru: string
|
why_ru: string
|
||||||
what_ru: string
|
what_ru: string
|
||||||
quick_start: string
|
quick_start?: string
|
||||||
features_en: Feature[]
|
features_en: Feature[]
|
||||||
features_ru: Feature[]
|
features_ru: Feature[]
|
||||||
access_url?: string
|
access_url?: string
|
||||||
|
|
@ -67,12 +67,13 @@ function generateReadme(args: CreateArgs): string {
|
||||||
const cloneLine = args.include_clone !== false
|
const cloneLine = args.include_clone !== false
|
||||||
? `git clone https://github.com/slaid098/${args.repo_name}.git\n`
|
? `git clone https://github.com/slaid098/${args.repo_name}.git\n`
|
||||||
: ""
|
: ""
|
||||||
const hasBashBlock = args.include_clone !== false || args.quick_start !== ""
|
const hasBashBlock = args.include_clone !== false || (args.quick_start ?? "") !== ""
|
||||||
|
const qs = args.quick_start ?? ""
|
||||||
const bashBlockEn = hasBashBlock
|
const bashBlockEn = hasBashBlock
|
||||||
? `\`\`\`bash\n${cloneLine}${args.quick_start}\n\`\`\``
|
? `\`\`\`bash\n${cloneLine}${qs}\n\`\`\``
|
||||||
: ""
|
: ""
|
||||||
const bashBlockRu = hasBashBlock
|
const bashBlockRu = hasBashBlock
|
||||||
? `\`\`\`bash\n${cloneLine}${args.quick_start}\n\`\`\``
|
? `\`\`\`bash\n${cloneLine}${qs}\n\`\`\``
|
||||||
: ""
|
: ""
|
||||||
const stepsEn = renderSteps(args.quick_start_steps_en)
|
const stepsEn = renderSteps(args.quick_start_steps_en)
|
||||||
const stepsRu = renderSteps(args.quick_start_steps_ru)
|
const stepsRu = renderSteps(args.quick_start_steps_ru)
|
||||||
|
|
@ -210,7 +211,7 @@ export default tool({
|
||||||
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'). Optional — omit or pass '' when setup is steps-only (use quick_start_steps_*); bash block omitted when empty AND include_clone is false."),
|
||||||
quick_start_steps_en: tool.schema
|
quick_start_steps_en: tool.schema
|
||||||
.array(tool.schema.string())
|
.array(tool.schema.string())
|
||||||
.optional()
|
.optional()
|
||||||
|
|
@ -294,7 +295,6 @@ export default tool({
|
||||||
what_en: args.what_en,
|
what_en: args.what_en,
|
||||||
why_ru: args.why_ru,
|
why_ru: args.why_ru,
|
||||||
what_ru: args.what_ru,
|
what_ru: args.what_ru,
|
||||||
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`
|
||||||
|
|
@ -304,6 +304,14 @@ 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`
|
||||||
|
|
||||||
|
const hasBash = args.include_clone !== false || (args.quick_start ?? "") !== ""
|
||||||
|
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
|
||||||
|
if (!hasBash && !hasStepsEn)
|
||||||
|
return `❌ quick_start or quick_start_steps_en required (Quick Start EN would be empty)`
|
||||||
|
if (!hasBash && !hasStepsRu)
|
||||||
|
return `❌ quick_start or quick_start_steps_ru required (Quick Start RU would be empty)`
|
||||||
|
|
||||||
const content = generateReadme({
|
const content = generateReadme({
|
||||||
repo_name: args.repo_name!,
|
repo_name: args.repo_name!,
|
||||||
tagline: args.tagline!,
|
tagline: args.tagline!,
|
||||||
|
|
@ -311,7 +319,7 @@ export default tool({
|
||||||
what_en: args.what_en!,
|
what_en: args.what_en!,
|
||||||
why_ru: args.why_ru!,
|
why_ru: args.why_ru!,
|
||||||
what_ru: args.what_ru!,
|
what_ru: args.what_ru!,
|
||||||
quick_start: args.quick_start!,
|
quick_start: args.quick_start,
|
||||||
features_en: args.features_en!,
|
features_en: args.features_en!,
|
||||||
features_ru: args.features_ru!,
|
features_ru: args.features_ru!,
|
||||||
access_url: args.access_url,
|
access_url: args.access_url,
|
||||||
|
|
|
||||||
66
docs/decisions/063-pr-149-empty-quick-start.md
Normal file
66
docs/decisions/063-pr-149-empty-quick-start.md
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
# ADR-063: Allow empty quick_start with steps-only Quick Start (PR-149)
|
||||||
|
|
||||||
|
## Статус
|
||||||
|
Accepted (2026-07-30)
|
||||||
|
|
||||||
|
## Контекст
|
||||||
|
ADR-062 (PR-146) добавил параметры `quick_start_steps_en/ru` и условный
|
||||||
|
`hasBashBlock` (`include_clone !== false || quick_start !== ""`), чтобы
|
||||||
|
поддержать userscript-README с кликабельными шагами вместо bash-команды. Однако
|
||||||
|
валидация в `execute()` (mode `create`) по-прежнему требовала непустой
|
||||||
|
`quick_start` через falsy-чек `!v` в required-Record — пустая строка `""`
|
||||||
|
считалась отсутствующей, тулза возвращала `quick_start is required for create
|
||||||
|
mode`. Скилл `repo-readme` (раздел 8 «Кейс: userscript / web-app / npm-package»)
|
||||||
|
документирует комбинацию `include_clone: false` + `quick_start: ""` +
|
||||||
|
`quick_start_steps_*` как supported — но тулза её отвергала. Дополнительно:
|
||||||
|
`hasBashBlock` использовал `args.quick_start !== ""`, что для `undefined`
|
||||||
|
давало `true` (undefined !== ""), и bash-блок рендерил literal `undefined`
|
||||||
|
в теле code-fence. ADR-062 явно отметил в «Альтернативах», что ослабление
|
||||||
|
required-чек — отдельное решение вне scope того PR.
|
||||||
|
|
||||||
|
## Решение
|
||||||
|
1. **`CreateArgs.quick_start`** — сделан optional (`quick_start?: string`),
|
||||||
|
чтобы отражать реальную опциональность параметра.
|
||||||
|
2. **Required-валидация в `execute()`** — `quick_start` убран из required-Record.
|
||||||
|
Вместо него добавлен guard после общих required-чеков:
|
||||||
|
- `hasBash = include_clone !== false || (quick_start ?? "") !== ""`
|
||||||
|
- `hasStepsEn = !!quick_start_steps_en && length > 0`
|
||||||
|
- `hasStepsRu = !!quick_start_steps_ru && length > 0`
|
||||||
|
- Если `!hasBash && !hasStepsEn` → ошибка `quick_start or
|
||||||
|
quick_start_steps_en required (Quick Start EN would be empty)`
|
||||||
|
- Если `!hasBash && !hasStepsRu` → аналогичная ошибка для RU.
|
||||||
|
Guard гарантирует, что Quick Start секция не пуста в ОБОИХ языках. Если
|
||||||
|
bash-блок рендерится — steps опциональны (backward compat).
|
||||||
|
3. **`hasBashBlock` и тела bash-блоков** — `args.quick_start ?? ""` вместо
|
||||||
|
`args.quick_start`: обрабатывает `undefined` (когда параметр не передан
|
||||||
|
вообще), иначе `undefined !== ""` даёт `true` и bash-блок рендерит literal
|
||||||
|
`undefined`. Локальная переменная `qs = args.quick_start ?? ""` переиспользуется
|
||||||
|
в обоих bash-блоках (EN/RU).
|
||||||
|
4. **Schema `.describe()`** — убрано "Required for create mode.", добавлено
|
||||||
|
пояснение: параметр optional, пустая строка/absent допустимы когда есть
|
||||||
|
`quick_start_steps_*`, bash-блок опускается при пустом AND `include_clone=false`.
|
||||||
|
5. **`validateReadme`** — без изменений: проверяет наличие строк "Quick Start" /
|
||||||
|
"Быстрый старт" в заголовках (`### ⚡ Quick Start` / `### ⚡ Быстрый старт`),
|
||||||
|
которые рендерятся всегда. README без bash-блока (только steps) проходит
|
||||||
|
валидацию. Подтверждено smoke-тестом (сценарий 1).
|
||||||
|
6. **Backward compatibility** — все существующие вызовы с непустым `quick_start`
|
||||||
|
и без steps работают идентично (сценарий 2 smoke-теста: git clone + pip
|
||||||
|
install, без шагов, без `undefined`).
|
||||||
|
|
||||||
|
## Альтернативы
|
||||||
|
- **Сделать `quick_start` required только когда нет steps** — отвергнуто:
|
||||||
|
сложная условная required-логика в одном Record, тяжело читать. Cleaner —
|
||||||
|
убрать из required и добавить явный guard с понятными сообщениями ошибок
|
||||||
|
для каждого языка отдельно.
|
||||||
|
- **Один общий guard «есть bash ИЛИ (steps EN И steps RU)»** — отвергнуто:
|
||||||
|
это допустило бы пустую RU-секцию когда есть bash + steps EN (но это
|
||||||
|
backward-compat кейс, где bash рендерится в обеих секциях). Текущий guard
|
||||||
|
(`!hasBash && !hasStepsX`) срабатывает ТОЛЬКО когда bash-блока нет —
|
||||||
|
тогда steps нужны в обоих языках. Когда bash есть — steps опциональны.
|
||||||
|
- **Валидировать steps в `validateReadme`** — отвергнуто: validate проверяет
|
||||||
|
структуру delimiter-пар и наличие секций, не содержимое Quick Start.
|
||||||
|
Заголовки рендерятся всегда; проверять пустоту секции — responsibility
|
||||||
|
`execute()`, не `validateReadme`.
|
||||||
|
- **Не вводить `qs` локальную переменную** — отвергнуто: повторение
|
||||||
|
`args.quick_start ?? ""` в трёх местах (hasBashBlock + 2 bash-блока) хуже
|
||||||
|
для читаемости, чем одна переменная.
|
||||||
20
docs/handoff/pr-149-empty-quick-start.md
Normal file
20
docs/handoff/pr-149-empty-quick-start.md
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
pr: 149
|
||||||
|
title: fix(create-readme): allow empty quick_start with steps-only Quick Start
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что сделано
|
||||||
|
|
||||||
|
Ослаблена валидация `quick_start` в `.opencode/tools/create-readme.ts` (mode `create`): параметр убран из required-Record (раньше falsy-чек `!v` отвергал `""`), вместо него добавлен guard — Quick Start секция не должна быть пустой (bash-блок ИЛИ steps в ОБОИХ языках EN/RU). `CreateArgs.quick_start` сделан optional (`?: string`). `hasBashBlock` и тела bash-блоков обрабатывают `undefined` через `?? ""` (раньше `undefined !== ""` давало `true` → bash-блок рендерил literal `undefined`). `.describe()` в schema обновлён (убрано "Required for create mode."). Smoke-тест: 5 сценариев, 24 assertion'а, все PASS (userscript-only-steps, backward-compat bash, guard EN, guard RU, bash+steps-en-no-steps-ru).
|
||||||
|
|
||||||
|
## Почему
|
||||||
|
|
||||||
|
Issue #147: тулза блокировала userscript-README без bash-команды (`quick_start: ""` + `include_clone: false`), хотя скилл `repo-readme` (раздел 8) документирует именно эту комбинацию. PR #146 (ADR-062) добавил `quick_start_steps_*` и условный `hasBashBlock`, но явно отметил в «Альтернативах», что ослабление required-валидации `quick_start` — вне scope того PR. PR #147 закрывает этот пробел: guard гарантирует, что Quick Start не пуст, при этом пустой `quick_start` (или его отсутствие) теперь допустим, если есть steps.
|
||||||
|
|
||||||
|
## Pending
|
||||||
|
|
||||||
|
—
|
||||||
|
|
||||||
|
## Watch out
|
||||||
|
|
||||||
|
Guard проверяет steps отдельно для EN и RU: если bash-блок не рендерится (`include_clone=false` + пустой `quick_start`), нужно `quick_start_steps_en` И `quick_start_steps_ru` — иначе одна из секций будет пустой. Если у репо один bash-блок (non-empty `quick_start` или `include_clone=true`) — steps опциональны в обоих языках (backward compat). `validateReadme` не менялся: он проверяет наличие строк "Quick Start"/"Быстрый старт" в заголовках, которые рендерятся всегда. ADR-063 отменяет альтернативу «Ослабить required-чек quick_start» из ADR-062.
|
||||||
|
|
@ -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
|
│ │ ├── 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 ?? "")
|
||||||
│ │ ├── 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