--- 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 (зафиксировано в комментарии теста).