opencode-config/docs/handoff/pr-65-tools-refactor-shared-module.md
Sergey 15fc7d014d
refactor(tools): add repo parameter + shared module + tunnel tests (#65)
* refactor(tools): extract shared module for gh spawnSync logic

* feat(tools): add repo parameter to 5 GitHub tools

* test(tools): add tunnel tool tests

* test(tools): add repo parameter test cases for 5 tools

* docs(handoff): scaffold handoff and ADR for PR

* docs(handoff): set PR number

* docs(project-map): update after PR#65 structural changes

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-07-25 19:19:19 +03:00

104 lines
No EOL
7.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
pr: 65
title: refactor(tools): add repo parameter + shared module + tunnel tests
---
## Что сделано
- **`.opencode/tools/_shared.ts`** — новый shared module: `parseRepo(repo?)`
(возвращает `["--repo", repo]` или `[]` для auto-detect), `runGh(args, repo?,
opts?)` (spawnSync с prepended `--repo` при явном repo + `cwd` из opts),
`formatResult(r, toolName)` (canonical `⚠️ <tool> failed (exit K): ...` на
ошибке, `stdout.trim()` на успехе). Устраняет ~50 строк дублирования
spawnSync-логики в 5 GitHub tools.
- **`tests/_ts_loader.mjs`** — расширение для поддержки relative imports
(`./_shared`): `stripTs` стриппает `import { ... } from "./..."` + `export`
на top-level declarations + type annotations в function params; новая
`inlineShared()` инлайнит код shared-модуля в `new Function` sandbox (с
авто-добавлением `.ts` расширения к specifier). Без этого loader падал на
ENOENT — local imports не резолвились.
- **5 GitHub tools отрефакторены** на `runGh`/`formatResult` + добавлен
опциональный `repo?: string` параметр (backward-compatible):
- `create-issue.ts``runGh` + `formatResult(r, "gh issue create")`
- `create-pr.ts``runGh` + `formatResult(r, "gh pr create")`
- `post-review.ts``runGh` (error-ветка оставлена кастомной с PR номером)
- `post-docs-review.ts``runGh` (аналогично)
- `merge-pr.ts``runGh` (аналогично)
При `repo` omitted → `parseRepo` возвращает `[]`, gh auto-detect'ит из
`context.worktree` (cwd) — поведение идентично pre-refactor (ADR-025 / PR#61).
- **Tunnel tests созданы** (issue #64 проблема 3 — единственный tool без
тестов):
- `tests/test_tunnel_tool.py` — 6 pytest-тестов: start без токена (exit 1),
start с токеном (`started (PID: N)`), start с `TUNNEL_DOMAIN` (`domain:
...`), stop при живом процессе (`stopped` + PID-файл удалён), stale PID-файл
(чистка + start), toggle (повторный start → stop). Изоляция: копия
`tunnel.sh` в tmp с переписанными PID/LOG путями + fake `cloudflared`
(sleep 30s) на PATH.
- `tests/test_tunnel_tool.ts` — 4 TS-теста: spawnSync с `bash` + путём к
скрипту, success → trimmed stdout, failure → `⚠️ tunnel failed (exit K)`,
cwd из context.
- **5 test-файлов обновлены** кейсами для `repo?: string` (явный / auto-detect /
invalid):
- `test_create_issue_tool.py/.ts` — +3 кейса (explicit, omitted, invalid)
- `test_create_pr_tool.py/.ts` — +3 кейса
- `test_post_review_tool.py/.ts` — +3 кейса
- `test_post_docs_review_tool.py/.ts` — +3 кейса
- `test_merge_pr_tool.py/.ts` — созданы с нуля (8 .py + 5 .ts: базовые
кейсы + repo; merge-pr не имел тестов ранее)
- **ADR-027** + этот handoff.
## Почему
Research (subagent explore, PR#63) выявил 3 проблемы в GitHub tools инфраструктуре:
1. **Дублирование spawnSync логики** — каждый из 5 GitHub tools дублировал
`spawnSync("gh", [...], { encoding, cwd })` + `if (r.status !== 0) return
"⚠️ ... failed (exit ...): ..."` — ~10 строк × 5 файлов = ~50 строк
дублирования. Shared module устраняет дублирование, единый canonical
error-формат.
2. **Нет `repo` параметра** — tools работали только через auto-detect (`gh`
определяет owner/repo из `git remote` в `context.worktree`). Ограничение:
из не-git-директории `gh` не может определить remote → fall back на
хардкод (баг issue #60, исправлен в PR#61). Явный `repo?: string` даёт
гибкость для pipeline оркестрации без потери auto-detect по умолчанию
(backward-compatible). Связано с ADR-025 (PR#61 убрал хардкод, этот PR
делает `repo` параметром — явный путь вместо хардкода).
3. **Tunnel без тестов**`tunnel` единственный из 10 tools без парных
`.py` + `.ts` тестов. Toggle-логика (PID-файл, kill -0, stale cleanup)
не покрыта → регрессии проходят незамеченными. 6 .py + 4 .ts тестов
покрывают все ветви `tunnel.sh`.
## Pending
- После merge: на хосте `git pull` + рестарт opencode-контейнера чтобы
подхватились обновлённые tools (auto-discovered через @opencode-ai/plugin).
- Зависимости: supersedes partial PR#61 (ADR-025) — убрал хардкод `--repo`,
этот PR параметризует; related PR#63 (ADR-026) — tool-usage policy
описывает 10 tools, этот PR улучшает их реализацию.
## Watch out
- **`_ts_loader.mjs` extension** — `inlineShared()` добавляет `.ts` к
relative import specifier если его нет (`./_shared``./_shared.ts`).
Это необходимо потому что TS import specifiers опускают расширение, а
`readFileSync` требует полный путь. Если будущие shared-модули используют
другие расширения (`.tsx`, `.mts`) — потребуется расширить логику.
- **`formatResult` только для create-issue/create-pr** — post-review,
post-docs-review, merge-pr сохраняют кастомный error с PR номером
(`⚠️ ... failed for PR #N (exit K): ...`) для диагностики. `formatResult`
дал бы `⚠️ ... failed (exit K): ...` без PR — потеря информации. Тесты
`test_gh_failure_returns_error` проверяют substring `"post-review failed"`
/ `"exit 1"`оба варианта проходят.
- **Backward-compat критична** — существующие `test_spawnsync_args` в
post-review/post-docs-review ассертят `"--repo" not in args`. При `repo`
omitted `parseRepo` возвращает `[]``--repo` не добавляется → тесты
проходят (51→62 на 4 tools). `test_repo_omitted_no_repo_flag` явно
закрепляет это поведение.
- **Tunnel test isolation** — `tunnel.sh` хардкодит `/tmp/tunnel.pid`. Тесты
копируют скрипт в `tmp_path` и переписывают PID/LOG пути через `replace()`.
Если в `tunnel.sh` изменится формат строк `PID_FILE="..."` — паттерн
replace сломается, тесты упадут (раннее обнаружение).
- **`commit.ts` НЕ в списке 5** — это `git`, не `gh` tool (нет `--repo`
флага). Оставлен как есть.
- **`.opencode/package-lock.json` untracked** — не относится к этому PR,
оставлен вне коммитов.