From 612446d65b572f7d0191f6f5dc0ec81ac692e1f3 Mon Sep 17 00:00:00 2001 From: "ki.sagidullin" Date: Fri, 21 Aug 2026 00:05:54 +0500 Subject: [PATCH 1/2] chore(scripts): add build-publish-ui.ps1 --- scripts/build-publish-ui.ps1 | 109 +++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 scripts/build-publish-ui.ps1 diff --git a/scripts/build-publish-ui.ps1 b/scripts/build-publish-ui.ps1 new file mode 100644 index 0000000..e752d36 --- /dev/null +++ b/scripts/build-publish-ui.ps1 @@ -0,0 +1,109 @@ +<# +build-publish-ui.ps1 — локальная сборка Windows-бинаря с UI (Fyne) и публикация в Gitea Packages. + +Почему локально: Fyne-окно требует cgo + C-компилятор (MinGW/gcc), которых нет в CI. +CI собирает ТОЛЬКО headless-linux (см. .gitea/workflows/ci.yaml). Windows-бинарь +с графическим окном собирается на Windows-машине Камиля и публикуется скриптом ниже +в ту же версию commit-, что и CI, с теми же companion-файлами (.version/.sha256), +чтобы автообновление (U4 checksum, ResolveLatest) работало без изменений. + +Требования: + - Go с доступным C-компилятором (CGO_ENABLED=1). WinLibs gcc: см. память + `mem:toolchain/cgo-winlibs-gcc` — bin нужно добавить в PATH перед запуском. + - Файл scripts/.gitea-creds (git-ignored, см. ниже) с токенами. + Формат (по одной `ключ=значение` на строку): + GITEA_TOKEN=write:packages-токен + UPDATE_TOKEN=read:package-токен + GIT_MAIN_URL=http://gitea.hal9000.home + - Ветка уже запушена в `main` (VERSION = commit- от текущего HEAD). + +Пример: + powershell -ExecutionPolicy Bypass -File scripts/build-publish-ui.ps1 +#> + +$ErrorActionPreference = "Stop" + +$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$RepoRoot = Split-Path -Parent $ScriptDir +$CredsFile = Join-Path $ScriptDir ".gitea-creds" +$Package = "ratatoskr" +$Owner = "kamelion" +$Filename = "ratatoskr-windows-amd64.exe" + +Set-Location $RepoRoot + +# --- 0. Проверка C-тулчейна --- +if ($env:CGO_ENABLED -eq "0") { Write-Error "CGO_ENABLED=0 — нужен C-компилятор (MinGW). Уберите его из env." } +if (-not (Get-Command gcc -ErrorAction SilentlyContinue)) { + Write-Host "gcc не найден в PATH. Пример (WinLibs):" + Write-Host ' $p = "<...>\mingw64\bin"; $env:Path = "$p;$env:Path"' + Write-Error "C-компилятор gcc не найден." +} +Write-Host "[ok] gcc: $((gcc --version | Select-Object -First 1))" + +# --- 1. Чтение секретов --- +if (-not (Test-Path $CredsFile)) { + Write-Error "Нет файла секретов: $CredsFile`nФормат: GITEA_TOKEN=... / UPDATE_TOKEN=... / GIT_MAIN_URL=..." +} +$creds = @{} +Get-Content $CredsFile | ForEach-Object { + $line = $_.Trim() + if ($line -and -not $line.StartsWith("#")) { + $kv = $line.Split("=", 2) + if ($kv.Count -eq 2) { $creds[$kv[0].Trim()] = $kv[1].Trim() } + } +} +$giteaToken = $creds["GITEA_TOKEN"] +$updateToken = $creds["UPDATE_TOKEN"] +$giteaUrl = $creds["GIT_MAIN_URL"] +if (-not $giteaToken) { Write-Error "В $CredsFile нет GITEA_TOKEN (нужен write:packages)." } +if (-not $giteaUrl) { Write-Error "В $CredsFile нет GIT_MAIN_URL." } + +# --- 2. Версия = commit- текущего HEAD (совпадает с именованием CI) --- +$sha = (git rev-parse --short HEAD).Trim() +if (-not $sha) { Write-Error "git rev-parse --short HEAD не дал хэш." } +$Version = "commit-$sha" +Write-Host "[build] VERSION=$Version FILENAME=$Filename" + +# --- 3. Сборка (cgo + UI) --- +$env:CGO_ENABLED = "1" +$out = Join-Path $RepoRoot $Filename +$ldflags = "-s -w -X main.version=${Version} -X main.updateToken=${updateToken}" +Write-Host "[build] go build -ldflags=... -o $Filename" +$goArgs = @("build", "-ldflags=$ldflags", "-o", $out, "./cmd/ratatoskr/") +go $goArgs +if ($LASTEXITCODE -ne 0) { Write-Error "go build завершился с кодом $LASTEXITCODE" } + +# --- 4. Companion-файлы (.version, .sha256) --- +$sha256Hex = (Get-FileHash -Algorithm SHA256 $out).Hash.ToLower() +$verFile = "$out.version" +$sumFile = "$out.sha256" +Set-Content -Path $verFile -Value $Version -NoNewline -Encoding ascii +Set-Content -Path $sumFile -Value $sha256Hex -NoNewline -Encoding ascii +Write-Host "[ok] bin: $out ($([math]::Round((Get-Item $out).Length/1MB,1)) MB)" +Write-Host "[ok] sha256: $sha256Hex" + +# --- 5. Публикация в Gitea Packages --- +$api = "$giteaUrl/api/packages/$Owner/generic/$Package/$Version" +$files = @($Filename, "$Filename.version", "$Filename.sha256") +foreach ($f in $files) { + Write-Host "[put] $api/$f" + curl.exe -sS -X PUT ` + -H "Authorization: token $giteaToken" ` + -H "Content-Type: application/octet-stream" ` + "$api/$f" ` + --data-binary "@$f" + if ($LASTEXITCODE -ne 0) { Write-Error "curl PUT $f завершился с кодом $LASTEXITCODE" } +} + +# --- 6. Самопроверка: читаем .sha256 обратно и сверяем (контракт U4) --- +Write-Host "[verify] перечитываю $Filename.sha256 из Gitea..." +$remoteSum = (curl.exe -sS -H "Authorization: token $giteaToken" "$api/$Filename.sha256").Trim() +if ($remoteSum -ne $sha256Hex) { + Write-Error "Проверка не сошлась: remote=$remoteSum local=$sha256Hex" +} +$remoteVer = (curl.exe -sS -H "Authorization: token $giteaToken" "$api/$Filename.version").Trim() +if ($remoteVer -ne $Version) { + Write-Error "Версия не сошлась: remote=$remoteVer local=$Version" +} +Write-Host "[ok] опубликовано и проверено: $Version/$Filename (+.version, +.sha256)" From 5c82229a00888e62fa0c8711302b666bc6bc88f8 Mon Sep 17 00:00:00 2001 From: "ki.sagidullin" Date: Fri, 21 Aug 2026 00:08:45 +0500 Subject: [PATCH 2/2] =?UTF-8?q?chore(serena):=20=D0=BE=D0=B1=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=B8=D1=82=D1=8C=20=D0=BF=D0=B0=D0=BC=D1=8F=D1=82=D1=8C?= =?UTF-8?q?=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0=20(onboarding)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .serena/memories/conventions.md | 24 ++++--- .serena/memories/core.md | 67 ++++++++----------- .serena/memories/suggested_commands.md | 17 +++-- .serena/memories/task_completion.md | 5 +- .serena/memories/tech_stack.md | 38 +++++------ .serena/memories/toolchain/cgo-winlibs-gcc.md | 13 ++++ 6 files changed, 87 insertions(+), 77 deletions(-) create mode 100644 .serena/memories/toolchain/cgo-winlibs-gcc.md diff --git a/.serena/memories/conventions.md b/.serena/memories/conventions.md index 1e7d79e..496f7bb 100644 --- a/.serena/memories/conventions.md +++ b/.serena/memories/conventions.md @@ -1,10 +1,12 @@ # conventions ## Стиль / кодстайл -- Стандартный Go-стиль; гофм `gofmt`/`go fmt ./...`. Документация-комментарии и - package-doc на русском языке (в START-комментариях файлов и doc-комментариях). -- Типизация: строгие типы, интерфейсы для абстракций (Decider, LiveProber). +- Стандартный Go-стиль; `gofmt`/`go fmt ./...`. Документация-комментарии и + package-doc на русском языке. +- Типизация: строгие типы, интерфейсы для абстракций (Decider, ui.Store, events.Event, chat.Channel). - Свой тип `config.Duration` для времени (YAML-строки "5s"/"10m"), метод `.Duration()`. +- UI: uni-directional data flow — UI читает снапшоты (копии, `ui.Store`), не мутирует storage/core. + Ядро публикует события в `events.Bus`, UI подписывается `events.Hub` → колбэки в UI-поток через `fyne.Do`. ## Обработка ошибок - Ошибки классифицируются по идентификаторам классов в исходниках (см. ниже). @@ -15,9 +17,10 @@ | Блок | Коды | Где | |---|---|---| | C | C1–C4 | internal/config | -| A | A1–A4 | internal/analyst | +| A | A1–A4 | internal/analyst; A (app): A1–A4 в internal/app | | M | M1–M5 | internal/chat | -| O | O1–O4 | internal/opencode | +| D | D1–D5 | internal/core (ErrDecideFailed, ErrMaxTurns, ErrCommandUnknown…) | +| O | O1–O5 | internal/opencode | | S | S1–S5 | internal/storage | | W | W1–W5 | internal/worker | | E | E1–E4 | internal/worker (репозитории) | @@ -25,15 +28,16 @@ | U | U1–U6 | internal/update | ## Архитектурные конвенции -- **Composition root** — internal/app; подсистемы собираются там, лимиты Core - (MaxTurns=15, MaxConfirmCycles=3, MaxQuestionsPerTurn=5) в core.New. -- **Агенты** (analyst.md/dev.md/reviewer.md) — markdown-промпты, встроены через go:embed - (internal/agents/*.md + embed.go), распаковываются в config_dir. Вердикт — строгий JSON. +- **Composition root** — internal/app; подсистемы собираются там. UI-окно создаётся через + `newUIWindow(store)` (build-tag: cgo → desktop.New, !cgo → nil/headless). Флаг `--noui` выключает окно. +- **Слои:** model (домен) ← storage/events/UI зависят только от model; core не знает про UI. +- **Агенты** (analyst.md/dev.md/reviewer.md) — markdown-промпты, go:embed, распаковка в config_dir. + Вердикт — строгий JSON. - Репо-клонирование/воркеры — через gitops; ветки задач `feat/` (см. mem:core). - Обратная совместимость: поле `Repo` (одиночный) и `Repos` (список); EffectiveRepos/ SetReposFromDB/ReposJoined в storage/models.go. ## Версии - `app.Version` — семантическая major.minor.patch (ручной инкремент: patch=фиксы, - minor=новая обратно-совместимая функциональность, major=несовместимые изменения). + minor=новая обратно-совместимая функциональность, major=несовместимые изменения). Сейчас 0.2.2. - `main.version` (ldflag) — build-идентификатор `commit-`, отдельно от app.Version. \ No newline at end of file diff --git a/.serena/memories/core.md b/.serena/memories/core.md index 822629e..b995955 100644 --- a/.serena/memories/core.md +++ b/.serena/memories/core.md @@ -1,70 +1,57 @@ # core Ratatoskr-go — оркестратор конвейера Ratatoskr (порт с Python на Go) в единый -статический бинарь (CGO_ENABLED=0). Субагенты запускаются через внешний процесс -[opencode](https://opencode.ai). Взаимодействие — Telegram-бот. +бинарь. Субагенты (analyst/dev/reviewer) запускаются через внешний процесс +[opencode](https://opencode.ai) (v2 HTTP API). Интерфейсы: Telegram-бот + Fyne UI. +Linux — статический headless-бинарь (CGO_ENABLED=0); Windows — с Fyne-окном (cgo). ## Структура (модули internal/) ``` -cmd/ratatoskr/ точка входа, сборка бинаря; main.version и main.updateToken вшиваются ldflag'ом +cmd/ratatoskr/ точка входа; флаги: -config, -version, -noui (headless). main.version/updateToken вшиваются ldflag'ом internal/ - app/ composition root/DI: App.New -> config.Load+Validate, ResolveExePaths, storage, Runner, Analyst, Core, Worker, Updater. packageOwner="kamelion", Version="0.1.0" + app/ composition root/DI: App.New -> config.Load+Validate, ResolveExePaths, storage, Runner, Analyst, Core, Worker, Updater, UI. packageOwner="kamelion", Version="0.2.2" config/ YAML+env загрузка (${VAR:-default}), defaults, validate C1-C4 + model/ доменные типы/контракты: Status + validTransitions (IsValidTransition/IsTerminal), TraceStatus. Нижний слой — не зависит от storage/events chat/ мультиканальный Router; telegram — long-poll канал. Коды M1-M5 core/ state-machine задач + Decider/analyst интерфейс. Коды D3/D4 analyst/ аналитик: промпт + разбор JSON-вердикта (opencode agent). Коды A1-A4 + events/ шина событий UI: Bus (buffered Sub/Pub), типизированный Hub, события TaskCreated/TaskStatusChanged/HistoryAppended/TraceAppended/AgentActivity, LogBus (панель «Логи») worker/ polling-планировщик + dev/reviewer-конвейер + gitops. Коды W*, E*, R* agents/ встроенные opencode-агенты (analyst.md, dev.md, reviewer.md) через go:embed opencode/ HTTP-клиент v2 API opencode serve, поллинг вердикта, LiveRegistry. Коды O1-O5 storage/ SQLite (modernc.org/sqlite, без CGO): tasks, traces, task_history. Коды S1-S5 update/ автообновление из Gitea Packages. Коды U1-U6 + ui/ desktop-оболочка: Store-абстракция (копии), Commands (UI->Core), Controller (шины->View), View/Window интерфейсы + ui/desktop/ Fyne-реализация окна (build-tag cgo, --noui / нет cgo → headless) +scripts/ build-publish-ui.ps1 — локальная Windows-сборка Fyne-бинаря + публикация в Gitea +docs/ ui-spec.md — спека Fyne UI (слои, event-bus, fyne.Do/снапшоты) ``` ## Ключевые инварианты -- **Команды/ядро:** Core.ProcessTurn — state-machine поверх storage. Команды: - /start /cancel /skip /retry N /status N /continue N. Активная задача — одна на чат. -- **Фазы аналитика (Decision.Phase):** `ask` (уточняющие вопросы), `propose` (правки - черновика), `ready` (черновик полон как есть), `abort` (тема вне проекта). propose и ready - в core обрабатываются одинаково. -- **Статусы задач:** draft→collecting→ready→approved→running→success/failed/timeout, - плюс cancelled/aborted/closed. approved — финальное одобрение («создавай»), после чего - воркер берёт задачу. IsValidTransition/IsTerminal в storage/models.go. -- **Decider/Worker/Analyst/Reviewer:** Decider=analyst интерфейс (analyst пакет реализует); - Worker — polling-планировщик dev-агента; Reviewer проверяет diff dev-ветки (R1-R6), - вердикт JSON {passed, critical_issues, solid_violations, comments}. -- **gitops (worker):** worker работает с worktrees; feature-ветка = `feat/` от - origin/main; ensureBranch (reset --hard + clean -fd + checkout -B), branchDiff - (`origin/main...`), push через http.extraHeader, токен как Bearer. -- **Пути «всё рядом с .exe»:** относительные db/worktree резолвятся от каталога бинаря - (ExeDir), не от cwd. config.yaml ищутся рядом с бинарём, фоллбэк cwd. -- **Автообновление:** авто = только Check+уведомление; замена — по /update. Версии в - Gitea Packages `commit-/` (не `latest/`). Вердикты агентов — строгий JSON. +- **App.New-сигнатура:** `App.New(configPath, version, updateToken string, noUI bool)` (4-й параметр — headless; cgo-вариант собирается только при наличии C-компилятора). +- **UI:** окно — ещё одна реализация `chat.Channel` (присоединяется в Router). Core не трогает UI; обмен — событийная шина (events). Кнопка «Завершить» = полный выход (SetOnQuit→cancel→UI.Run возвращается); закрытие крестиком = сворачивание, Core живёт. UI собирается с `--noui`/без cgo. +- **Фазы аналитика (Decision.Phase):** `ask`, `propose`, `ready` (два последних обрабатываются одинаково в core), `abort`. Требования валидатора: ask — chat_reply/questions; propose — хотя бы одно изменённое поле; ready — без изменённых полей. +- **Статусы задач (internal/model):** draft→collecting→ready→approved→running→success/failed/timeout + cancelled/aborted/closed (терминальные). UserID — chat.ID (одна активная задача на чат). +- **Decider/Worker/Analyst/Reviewer:** Decider=analyst интерфейс; Worker — polling-планировщик; Reviewer проверяет diff dev-ветки (R1-R6), вердикт JSON {passed, critical_issues, solid_violations, comments}. +- **gitops (worker):** worktree-режим; feature-ветка `feat/` от origin/main; push через http.extraHeader, токен Bearer. +- **Пути «всё рядом с .exe»:** db/worktree резолвятся от ExeDir; config.yaml — рядом с бинарём, фоллбэк cwd. +- **Автообновление:** авто = только Check+уведомление; замена — по /update; версии в `commit-/` (не `latest/`); Verify сверяет предprod-версию (binary+в.в) . ## opencode (v2 HTTP API, >= 1.18.18) -- Интеграция с субагентами — через headless `opencode serve`, **v2 API** (префикс `/api/*`). - Минимальная версия opencode **>= 1.18.18** (старый бинарь — только `/global/health`, не годится; - healthcheck падает с понятной ошибкой, класс O1). -- **Хардпин модели:** при создании сессии читается top-level `"model"` из конфига opencode - (`internal/opencode/config.go`, JSONC-стрип `//`/`/* */`/trailing-запятых) и передаётся в - `POST /api/session` как `{"model":{providerID,id}}` (разбор `provider/id` по первому `/`). -- **Класс O5 WARN (устойчивость к v1-конфигу):** конфиг по старой v1-схеме - (`provider.X.npm`/`options`) молча игнорируется v2 → провайдер без api → модель unsupported → - fallback. Раtatoskr не чинит это сам, но логирует warning: конфиг не читается/нет `model`, - и/или фактическая модель ответа (из assistant-сообщения) ≠ ожидаемой. Правильный v2-вид: - `api:{type:"aisdk",package,url}` и `request.headers` вместо `options.headers`. -- **Поллинг вердикта:** `POST /api/session/:id/prompt` (durable admit, неблокирующий) → - `GET /api/session/:id/message?order=desc&limit=200` (новые assistant-сообщения, текст в - `content[].type=="text"`) → завершение = `GET /api/session/active` без сессии + финальное - assistant-сообщение, стабильное `settlePolls=2` опроса. `POST .../interrupt` вместо abort. -- `ModelRef{ProviderID,ID,Variant}` — аналог v2 Model.Ref; `MinVersion="1.18.18"` в server.go. +- Интеграция с субагентами — через headless `opencode serve`, **v2 API** (`/api/*`). Версия opencode >= 1.18.18. +- **Хардпин модели:** при создании сессии читается top-level `model` из конфига opencode (`internal/opencode/config.go`, JSONC-стрип) и передаётся в `POST /api/session` как `{"model":{providerID,id}}`. +- **О5 WARN (устойчивость к v1-конфигу):** конфиг по старой схеме молча игнорируется v2; провайдер без api → unsupported модели → fallback. Ratatoskr не чинит сам, но логирует warning; фактическая модель ответа сравнивается с ожидаемой. Правильный v2-вид: `api:{type:"aisdk",package,url}`, `request.headers` вместо `options.headers`. +- **Поллинг вердикта:** `POST /api/session/:id/prompt` (durable admit) → `GET /api/session/:id/message?order=desc&limit=200` (новые assistant-сообщения, текст в `content[].type=="text"`) → завершение = `GET /api/session/active` без сессии + финальное assistant-сообщение, стабильное `settlePolls=2` опроса. `POST .../interrupt` вместо abort. ## Контракты (не ломать) -- `App.New(configPath, version, updateToken string)` — сигнатура. +- `App.New(configPath, version, updateToken string, noUI bool)` — сигнатура. - `packageOwner` — константа "kamelion" (не плодить vars/ldflag/конфиг). - `update.Updater` — создаётся структурой `&update.Updater{...}`, конструктора нет. +- `ui.Store` — чтение-модель для UI: ListTasks/GetTask/GetHistory/GetTraces (копии, без ссылок на storage). +- `events.Bus`/`events.LogBus` — шина UI (см. internal/events). publisher-интерфейсы подключаются к Core/Worker. -См. также `mem:tech_stack`, `mem:conventions`, `mem:task_completion`, `mem:suggested_commands`. \ No newline at end of file +см. также `mem:tech_stack`, `mem:conventions`, `mem:task_completion`, `mem:suggested_commands`. \ No newline at end of file diff --git a/.serena/memories/suggested_commands.md b/.serena/memories/suggested_commands.md index b00ffa5..020ed10 100644 --- a/.serena/memories/suggested_commands.md +++ b/.serena/memories/suggested_commands.md @@ -6,22 +6,27 @@ - `go fmt ./...` (или `make fmt`). - `go build -o ratatoskr ./cmd/ratatoskr/` (или `make build`). - `./ratatoskr -config config.yaml` (или `make run`). -- `./ratatoskr -version` — показать версию бинаря. -- Кросс-сборка: `make cross` (linux/amd64 + windows/amd64). +- `./ratatoskr -version` — версия бинаря; `./ratatoskr -noui` — headless (без окна). +- Кросс-сборка: `make cross` (linux/amd64 + windows/amd64, headless). + +## Windows / UI (Fyne) +- Сборка с UI (cgo): `powershell -ExecutionPolicy Bypass -File scripts/build-publish-ui.ps1`. + Требует `scripts/.gitea-creds` (не коммитить) и MinGW/gcc в PATH. См. `mem:toolchain/cgo-winlibs-gcc`. +- Обычная headless-сборка на Windows без cgo: `go build -o ratatoskr.exe ./cmd/ratatoskr/`. ## Go-тулчейн Хост без `go` в PATH — экспорт вручную: ``` export PATH=/opt/data/.local/go/bin:$PATH ``` -На машине разработки (Windows, cmd) — обычный system Go. +На машине разработки (Windows) — обычный system Go. ## git (worktree-процесс Ratatoskr) -- Feature-ветка: `feat/`, база — `origin/main`. Пример проверки diff всей ветки: +- Feature-ветка: `feat/`, база — `origin/main`. Проверка diff всей ветки: `git diff origin/main...HEAD`. - Проверить состав отслеживаемых файлов (например наличие .serena): `git ls-files`. - Статус: `git status`. Лог: `git log --oneline -10`. ## Замечания про среду -- ОС Windows + PowerShell 5.1 (shell: powershell) — команды собирать/запускать с учётом - этом (нет `&&`; использовать `;`/`if ($?) {}`; & для путей с пробелами). \ No newline at end of file +- ОС Windows + PowerShell 5.1 (shell: powershell) — нет `&&`; использовать `;`/`if ($?) {}`; + `&` для вызова путей с пробелами. Для серенных файлов лучше отдельная команда `serena memories check`. \ No newline at end of file diff --git a/.serena/memories/task_completion.md b/.serena/memories/task_completion.md index 87d4220..d8c6776 100644 --- a/.serena/memories/task_completion.md +++ b/.serena/memories/task_completion.md @@ -6,9 +6,10 @@ 2. **Тесты:** `go test ./... -v -count=1 -timeout 120s` — все проходят (`make test`). 3. **Статический анализ:** `go vet ./...` — чисто (`make vet`). 4. **Сборка:** `go build -o ratatoskr ./cmd/ratatoskr/` (`make build`) — компилируется. - (Кросс-сборка `make cross` — только при необходимости.) + - UI-сборка (cgo+Fyne) проверяется отдельно (`scripts/build-publish-ui.ps1` или `go build` с MinGW); + headless-сборка cgo-пакетов (`internal/ui/desktop`) — только typecheck. 5. **Пересмотр контрактов:** если менялась сигнатура `App.New(configPath, version, - updateToken string)` — обновить вызовы в `internal/app/app_test.go` (иначе go vet падает). + updateToken string, noUI bool)` — обновить все вызовы (app_test.go, main.go). 6. Коммит осмысленными атомарными коммитами в feature-ветку `feat/` от origin/main. Рабочий процесс Ratatoskr (агент dev в этом конвейере): изучить код, реализовать так, чтобы diff --git a/.serena/memories/tech_stack.md b/.serena/memories/tech_stack.md index f135264..c4da5f7 100644 --- a/.serena/memories/tech_stack.md +++ b/.serena/memories/tech_stack.md @@ -1,32 +1,32 @@ # tech_stack ## Язык / рантайм -- Go **1.25.0** (go.mod `go 1.25.0`). Модуль `github.com/kamelion/ratatoskr-go`. -- Сборка: статический бинарь, `CGO_ENABLED=0`. Локальный Go-тулчейн на хосте: - `export PATH=/opt/data/.local/go/bin:$PATH` (go1.25.0 linux/amd64). +- Go **1.25.0** (go.mod `go 1.25.0`, module `github.com/kamelion/ratatoskr-go`). +- Headless-сборка — статический бинарь `CGO_ENABLED=0`. Windows-сборка с UI — cgo (Fyne/GLFW) + MinGW-w64/gcc. +- Локальный Go-тулчейн на хосте: `export PATH=/opt/data/.local/go/bin:$PATH` (go1.25.0 linux/amd64). На Windows — обычный system Go. ## Основные зависимости (go.mod) +- `fyne.io/fyne/v2 v2.6.0` — desktop UI (cgo/GLFW; платформа Windows + Linux). Не собирается в CI (headless). - `gopkg.in/yaml.v3 v3.0.1` — парсинг config.yaml. - `modernc.org/sqlite v1.56.0` — SQLite без CGO (чистый Go). -- (indirect) google/uuid, go-humanize, mattn/go-isatty, x/sys, modernc.org/libc/mathutil/memory. +- (indirect) google/uuid, go-humanize, go-text/typesetting, x/sys, x/net, modernc.org/libc и пр. ## Внешние процессы -- **opencode** (opencode.ai) — внешний процесс для субагентов (analyst/dev/reviewer). - Управляется через internal/opencode (Runner, LiveRegistry). Настраивается в конфиге - (opencode.bin / config / config_dir / hard_timeout / idle_timeout / poll_ms). - Требуемая версия opencode: **>= 1.18.18** (v2 HTTP API `/api/*`; хардпин model из конфига — - `internal/opencode/config.go`, классы O1/O5). Подробно — `mem:core`. +- **opencode** (opencode.ai) — субагенты (analyst/dev/reviewer). Управляется через internal/opencode (Runner, Pool, LiveRegistry). Требуемая версия **>= 1.18.18** (v2 HTTP API `/api/*`). +- Настройки в конфиге: `opencode.bin / config / config_dir / hard_timeout / idle_timeout / poll_ms` + `opencode.server` (hostname, port, password). +- Подробно — `mem:core`. ## Сборка / Makefile -- `make build` — go build -ldflags="-s -w -X main.version=commit- -X main.updateToken=..." -o ratatoskr ./cmd/ratatoskr/. -- `make test` — go test ./... -v -count=1 -timeout 120s. -- `make vet` — go vet ./... `make fmt` — go fmt ./... -- `make run` — build + ./ratatoskr -config config.yaml. -- `make cross` — кросс-сборка linux/amd64 + windows/amd64 (ratatoskr-windows-amd64.exe — Windows-машина Камиля). -- GIT_SHA вшивается в main.version; UPDATE_TOKEN — в main.updateToken (секрет только у CI). +- `make build` — go build `-ldflags="-s -w -X main.version=commit- -X main.updateToken=..." -o ratatoskr ./cmd/ratatoskr/`. +- `make test` — `go test ./... -v -count=1 -timeout 120s`. +- `make vet` — `go vet ./...`; `make fmt` — `go fmt ./...`. +- `make run` — build + `./ratatoskr -config config.yaml`. +- `make cross` — linux/amd64 + windows/amd64 (headless, БЕЗ Fyne-UI; для UI нужен cgo). +- **Fyne-UI (Windows) собирается вручную** на машине с MinGW: + `scripts/build-publish-ui.ps1` (cgo) — сборка + публикация в Gitea в версию `commit-`. + Требует `scripts/.gitea-creds` (git-ignored) и gcc в PATH. См. `mem:toolchain/cgo-winlibs-gcc`. ## CI (.gitea/workflows/ci.yaml) -- Job `test`: go test ./... + go vet ./.... -- Job `build-and-package` (matrix linux/amd64+windows/amd64): собирает и публикует в - Gitea Packages на `main` в версию `commit-/` + companion-файлы `.version`/`.sha256`. -- Секреты: TC_GITEA_TOKEN (write:packages), TC_UPDATE_TOKEN (read:package), GIT_MAIN_URL. \ No newline at end of file +- Job `test`: `go test ./...` + `go vet ./...` (setup-go '1.23', check-latest). +- Job `build-and-package` (matrix linux/amd64 + windows/amd64): собирает headless-бинарь с ldflag update_token, публикует на `main` в версию `commit-/` (+ companion `.version`/`.sha256`). +- Секреты: `TC_GITEA_TOKEN` (write:packages), `TC_UPDATE_TOKEN` (read:package), vars `GIT_MAIN_URL`. \ No newline at end of file diff --git a/.serena/memories/toolchain/cgo-winlibs-gcc.md b/.serena/memories/toolchain/cgo-winlibs-gcc.md new file mode 100644 index 0000000..f79cc31 --- /dev/null +++ b/.serena/memories/toolchain/cgo-winlibs-gcc.md @@ -0,0 +1,13 @@ +# cgo/winlibs-gcc + +## MinGW/WinLibs для Fyne-сборки на Windows +- UI (internal/ui/desktop, fyne.io/fyne/v2) требует cgo + C-компилятор для GLFW. +- Используется **WinLibs** (mingw64) gcc. Bin каталог нужно добавить в PATH перед + `scripts/build-publish-ui.ps1`: + ```powershell + $p = "<...>\mingw64\bin"; $env:Path = "$p;$env:Path" + ``` +- Проверка: `gcc --version`; также убедиться, что `CGO_ENABLED` не = "0". +- Care: не использовать Cygwin/MSYS-сборки gcc — только native MinGW-w64 (WinLibs). +- Сборка UI выполняется локально на Windows-машине, публикуется в Gitea Packages + в в версию `commit-` со companion-.version/.sha256 (контракт U4) — см. скрипт и `mem:tech_stack`. \ No newline at end of file