opencode-config/docs/decisions/073-pr-175-overwrite-existing-local-readme.md
Sergey 4258621def
fix(repo-readme): create mode does not overwrite existing local README (#175)
* 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>
2026-07-31 21:10:29 +03:00

3.6 KiB
Raw Blame History

ADR-073: create-readme local-mode path resolution via context.worktree

Статус

Accepted (2026-07-31)

Контекст

Тулза create-readme (.opencode/tools/create-readme.ts) работает в двух режимах: локальном (file_path, без repo) и удалённом (repo: owner/name через gh api). В удалённом режиме spawnSync вызовы корректно передавали cwd: context.worktree — рабочую директорию сессии пользователя. В локальном режиме writeFileSync(file_path, ...) / readFileSync(file_path, ...) вызывались с относительным file_path (default README.md), который резолвился относительно CWD процесса плагина Bun, а не относительно context.worktree. В продакшене CWD плагина ≠ рабочая директория пользователя — файл писался/читался не туда, отчёт README.md created at README.md был ложным success (issue #148).

Асимметрия: spawnSync получал cwd, а fs.*Sync — нет. Тестовый лоадер tests/_ts_loader.mjs не поддерживал import { readFileSync, writeFileSync } from "fs" и расширенный TS-синтаксис (многострочные type-алиасы, non-null assertions, type annotations в переменных) — локальный режим тулзы нельзя было протестировать.

Решение

  1. Резолв пути относительно worktree: const absPath = path.resolve(context.worktree, file_path) — симметрично с spawnSync в удалённом режиме. writeFileSync(absPath, ...) / readFileSync(absPath, ...) работают с абсолютным путём. Существующий файл гарантированно перезаписывается.

  2. Расширение _ts_loader.mjs: stripTs теперь обрабатывает fs импорты, многострочные type X = { ... }, type annotations в const/let/var, non-null assertions x!. loadTool(spawnSyncImpl, fsImpl) передаёт fs модуль в sandbox. Это открывает возможность тестировать любую тулзу, использующую fs в локальном режиме.

Альтернативы

  • process.chdir(context.worktree) в начале execute(): меняло бы CWD процесса, но побочные эффекты на другие тулзы в том же процессе неприемлемы (гонки, неявное состояние). Отвергнуто.

  • Передача file_path как абсолютного всегда (валидировать на входе): ломало бы существующие вызовы с относительным file_path (default README.md). path.resolve(worktree, file_path) прозрачно обрабатывает оба случая (относительный → относительно worktree; абсолютный → как есть).

  • Не расширять лоадер, тестировать только через интеграцию: медленнее, хрупче, не ловит регрессии на уровне execute(). Расширение лоадера позволяет точечные unit-тесты с реальным fs во временных директориях.