* fix(create-readme): resolve file_path against context.worktree * test(loader): support fs imports and extended TS stripping * test(create-readme): add regression tests for local overwrite * docs(repo-readme): note file_path resolves against worktree * docs(handoff): set PR number --------- Co-authored-by: opencode-agent <agent@opencode.local>
108 lines
No EOL
7.2 KiB
Markdown
108 lines
No EOL
7.2 KiB
Markdown
---
|
||
pr: 175
|
||
title: fix(repo-readme): create mode does not overwrite existing local README
|
||
---
|
||
|
||
## Что сделано
|
||
|
||
Фикс бага #148 в тулзе `create-readme` (`.opencode/tools/create-readme.ts`):
|
||
локальный режим (`file_path`, без `repo`) вызывал `writeFileSync(file_path, ...)`
|
||
и `readFileSync(file_path, ...)` с относительным `file_path` (default
|
||
`README.md`), который резолвился относительно CWD процесса плагина Bun, а НЕ
|
||
относительно `context.worktree` (рабочей директории сессии пользователя).
|
||
Результат: отчёт `README.md created at README.md` — ложный success, файл на
|
||
диске не менялся.
|
||
|
||
Root cause: `spawnSync` вызовы в удалённом режиме корректно передавали
|
||
`cwd: context.worktree`, но `fs.*Sync` в локальном режиме — нет, пути
|
||
резолвились не там.
|
||
|
||
Изменения:
|
||
|
||
- `.opencode/tools/create-readme.ts`:
|
||
- Добавлен `import path from "path"`.
|
||
- `absPath = path.resolve(context.worktree, file_path)` — путь резолвится
|
||
относительно рабочей директории сессии.
|
||
- `writeFileSync(absPath, ...)` и `readFileSync(absPath, ...)` — запись/чтение
|
||
в/из корректной директории. Существующий README гарантированно
|
||
перезаписывается (создаётся новый если не существует).
|
||
- Удалённый режим (`args.repo`) не тронут — уже использовал
|
||
`cwd: context.worktree` для `spawnSync`.
|
||
|
||
- `tests/_ts_loader.mjs` — расширение лоадера для тестирования `create-readme.ts`
|
||
(локальный режим использует `fs`):
|
||
- `stripTs`: обработка `import { readFileSync, writeFileSync } from "fs"` →
|
||
`const { ... } = require("fs")`.
|
||
- `stripTs`: `g`-флаг для стрипа `type X = ...` (несколько алиасов в одном
|
||
файле: `Feature`, `CustomSection`, `CreateArgs`).
|
||
- `stripTs`: стрип многострочных `type X = { ... }` object-типов.
|
||
- `stripTs`: обобщение стрипа return type (объектный литерал как return type
|
||
`validateReadme(...): { ok: boolean; issues: string[] }`).
|
||
- `stripTs`: стрип type annotations в `const/let/var x: Type = ...`.
|
||
- `stripTs`: стрип TS non-null assertions `x!` → `x` (не трогает `!=`, `!==`,
|
||
унарный `!foo`).
|
||
- `loadTool(spawnSyncImpl, fsImpl)`: `fs` передаётся в sandbox через `new
|
||
Function(..., "fs", ...)` и `require("fs")` shim возвращает модуль
|
||
(настоящий `node:fs` по умолчанию или mock).
|
||
|
||
- `tests/test_create_readme_tool.py` — новый файл, 14 регрессионных тестов:
|
||
- `test_loader_can_load_tool` — sanity (args declared).
|
||
- `test_local_create_overwrites_existing_readme` — регрессия #148: существующий
|
||
README перезаписывается (mtime обновляется, delimiter-теги присутствуют,
|
||
старый контент отсутствует).
|
||
- `test_local_create_creates_new_readme` — создаёт новый файл.
|
||
- `test_local_create_custom_subdir_file_path` — пишет в поддиректорию.
|
||
- `test_remote_create_makes_two_spawnsync_calls` — удалённый режим: 2 вызова
|
||
(GET sha + PUT), не сломан фиксом.
|
||
- `test_remote_create_passes_worktree_cwd` — `cwd=context.worktree` в opts.
|
||
- `test_local_validate_reads_file_path` — локальный validate читает из
|
||
`file_path`.
|
||
- `test_local_validate_reports_missing_delimiters` — malformed README → issues.
|
||
- 6 валидационных ошибок: missing repo_name, missing tagline_ru, uppercase
|
||
repo_name, latin tagline_ru, cyrillic tagline_en, empty features_en.
|
||
|
||
- `.opencode/skills/repo-readme/SKILL.md` — раздел 3: уточнение, что `file_path`
|
||
резолвится относительно `context.worktree` и существующий файл
|
||
перезаписывается.
|
||
|
||
- `docs/project-map/README.md` — обновлены описания `create-readme.ts`,
|
||
`_ts_loader.mjs`, добавлен `test_create_readme_tool.py`.
|
||
|
||
## Почему
|
||
|
||
Issue #148: `create-readme` (mode `create`, локальный режим, без `repo`)
|
||
возвращал `README.md created at README.md`, но файл на диске не изменялся —
|
||
`stat` показывал прежний mtime, delimiter-теги отсутствовали. Скилл
|
||
`repo-readme` (раздел 6) обещает «перезапишет README.md (локально) через
|
||
`fs.writeFileSync`» — фактически ложный success. Тот же root cause затрагивал
|
||
`validate` (читал из неверного пути, не находил файл → ошибка вместо
|
||
валидации реального README).
|
||
|
||
Фикс `path.resolve(context.worktree, file_path)` гарантирует, что путь
|
||
резолвится относительно рабочей директории сессии (как и `spawnSync` в
|
||
удалённом режиме), а не относительно CWD процесса плагина. Перешёл от
|
||
«relative path против CWD процесса» к «absolute path относительно worktree» —
|
||
симметрично с тем, как `spawnSync` получает `cwd`.
|
||
|
||
## Pending
|
||
|
||
—
|
||
|
||
## Watch out
|
||
|
||
Лоадер `tests/_ts_loader.mjs` расширен для поддержки `fs` и расширенного
|
||
TS-стриппинга (многострочные type-алиасы, non-null assertions, type
|
||
annotations в переменных). Эти расширения безопасны для существующих тулз
|
||
(проверено: `commit.ts`, `draw-image.ts`, `create-pr.ts`,
|
||
`create-issue.ts` — все загружаются и 459 тестов проходят). Но если будущая
|
||
тулза использует более сложный TS-синтаксис (generics в вызовах, conditional
|
||
types, template literal types), лоадер может потребовать дальнейшего
|
||
расширения `stripTs`.
|
||
|
||
Тесты локального режима используют абсолютный `file_path` во временной
|
||
директории (`tempfile.TemporaryDirectory`), чтобы избежать перезаписи
|
||
README репозитория (лоадер хардкодит `worktree: REPO_ROOT`, относительный
|
||
путь `README.md` резолвился бы в `REPO_ROOT/README.md`). Для полноценного
|
||
теста `path.resolve(context.worktree, relative)` потребовалось бы параметризовать
|
||
`worktree` в лоадере — оставлено как future improvement (зафиксировано в
|
||
комментарии теста). |