feat(reader): Gitea client + config + board orchestrator

closes board-viewer-gitea-reader

- src/gitea.ts: HTTP client (getFile, getLatestCommitIso, rawUrl); DI'd fetch
- src/reader.ts: readBoard(client, repos) → TaskRecord[]
- src/config.ts: TOML config loader with board_viewer_repos whitelist
- 25 tests total (9 parser incl. 3 real-repo fixtures, 7 gitea, 5 reader, 4 config)
- emoji contract 🔴🟡🔵🟢 (open/in_progress/paused/blocked/done)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
vitya
2026-05-22 12:48:37 +03:00
parent 1c7e372abf
commit d093693795
14 changed files with 2543 additions and 15 deletions

View File

@@ -9,19 +9,18 @@ _Updated: 2026-05-22_
---
## [board-viewer-gitea-reader] — Gitea API reader → normalized task structure
**Status:** ready
**Where I stopped:** разблокирована (pointers done); реализация не начата. Это первая импл-таска, остальные impl-таски зависят от её типов.
**Next action:** TDD по контракту в `board-viewer-gitea-reader.md`.
## 🟢 [board-viewer-gitea-reader] — Gitea API reader → normalized task structure
**Status:** done
**Where I stopped:** TaskRecord контракт реализован (parser+gitea+reader+config); 25 тестов; typecheck clean. Тесты используют 3 реальных STATUS.md fixture'а.
**Next action:** разблокировать html-render — потребляет `readBoard(client, repos) → TaskRecord[]`.
**Branch:** master
---
## ⚪ [board-viewer-html-render] — render 5-col kanban from normalized structure
**Status:** ready
**Where I stopped:** ждёт `board-viewer-gitea-reader` (использует его типы).
**Next action:** после ready-стейта reader'а TDD по контракту в `board-viewer-html-render.md`.
**Blocker:** board-viewer-gitea-reader
**Where I stopped:** reader готов, импортируется `TaskRecord` из `src/reader.ts`. Импл не начата.
**Next action:** TDD по контракту в `board-viewer-html-render.md`.
**Branch:** master
---

View File

@@ -10,10 +10,12 @@
## Key files
- (TBD) `src/gitea-reader.{ts,py}`main reader module
- (TBD) `src/types.{ts,py}` — exported task structure
- (TBD) `tests/fixtures/` — sample STATUS.md + per-task.md pairs from real repos
- (TBD) `tests/gitea-reader.test.{ts,py}` — TDD assertions
- `src/parser.ts`STATUS.md → `StatusBlock[]` (pure)
- `src/gitea.ts` — Gitea HTTP client (DI'd fetch, `getFile` / `getLatestCommitIso` / `rawUrl`)
- `src/reader.ts``readBoard(client, repos) → TaskRecord[]`; orchestrates parser+client
- `src/config.ts` — loads `~/.config/projects-mcp/auth.toml``{ baseUrl, token, repos }`
- `tests/fixtures/``status-single-block.md`, `status-multi.md`, real STATUS.md from books / claude-skills / projects-meta-mcp
- `tests/parser.test.ts` (9), `tests/gitea.test.ts` (7), `tests/reader.test.ts` (5), `tests/config.test.ts` (4) — 25 tests total
## Acceptance criteria
@@ -48,13 +50,18 @@
## Open questions
- [ ] TS or Python? (повлияет на структуру cron-deploy).
- [ ] Owner-фильтр: брать репо из `gitea_owners` целиком или whitelist? Возможно нужен `board_viewer_repos` отдельным полем в auth.toml.
- [ ] Кэшировать ответы Gitea API локально между cron-тиками? (опт.: rate-limit, hot-reload).
- [x] TS (Node 22, vitest, ESM) — выбор подтверждён юзером 2026-05-22.
- [x] Whitelist `board_viewer_repos` в `auth.toml` — выбран как явный и контролируемый.
- [x] Без кэширования на MVP — Gitea на том же VDS, latency negligible.
## Completed steps
- [ ] (фиксируется при выполнении)
- [x] парсер STATUS.md (`src/parser.ts`) с маппингом ⚪🔴🟡🔵🟢 → open/in_progress/paused/blocked/done
- [x] Gitea HTTP client (`src/gitea.ts`): getFile / getLatestCommitIso / rawUrl, DI fetch
- [x] reader orchestrator (`src/reader.ts`): объединяет parser+client → TaskRecord[]
- [x] TOML config loader (`src/config.ts`) с whitelist `board_viewer_repos`
- [x] 25 тестов: 9 парсера (включая 3 real-repo фикстуры), 7 gitea, 5 reader, 4 config
- [x] typecheck clean
## Notes