diff --git a/openspec/changes/sdlc-rename/.openspec.yaml b/openspec/changes/archive/2026-10-24-sdlc-rename/.openspec.yaml similarity index 100% rename from openspec/changes/sdlc-rename/.openspec.yaml rename to openspec/changes/archive/2026-10-24-sdlc-rename/.openspec.yaml diff --git a/openspec/changes/sdlc-rename/design.md b/openspec/changes/archive/2026-10-24-sdlc-rename/design.md similarity index 100% rename from openspec/changes/sdlc-rename/design.md rename to openspec/changes/archive/2026-10-24-sdlc-rename/design.md diff --git a/openspec/changes/sdlc-rename/proposal.md b/openspec/changes/archive/2026-10-24-sdlc-rename/proposal.md similarity index 100% rename from openspec/changes/sdlc-rename/proposal.md rename to openspec/changes/archive/2026-10-24-sdlc-rename/proposal.md diff --git a/openspec/changes/sdlc-rename/specs/data-model/spec.md b/openspec/changes/archive/2026-10-24-sdlc-rename/specs/data-model/spec.md similarity index 100% rename from openspec/changes/sdlc-rename/specs/data-model/spec.md rename to openspec/changes/archive/2026-10-24-sdlc-rename/specs/data-model/spec.md diff --git a/openspec/changes/sdlc-rename/specs/hub-api/spec.md b/openspec/changes/archive/2026-10-24-sdlc-rename/specs/hub-api/spec.md similarity index 100% rename from openspec/changes/sdlc-rename/specs/hub-api/spec.md rename to openspec/changes/archive/2026-10-24-sdlc-rename/specs/hub-api/spec.md diff --git a/openspec/changes/sdlc-rename/specs/mcp/spec.md b/openspec/changes/archive/2026-10-24-sdlc-rename/specs/mcp/spec.md similarity index 100% rename from openspec/changes/sdlc-rename/specs/mcp/spec.md rename to openspec/changes/archive/2026-10-24-sdlc-rename/specs/mcp/spec.md diff --git a/openspec/changes/sdlc-rename/specs/migration/spec.md b/openspec/changes/archive/2026-10-24-sdlc-rename/specs/migration/spec.md similarity index 100% rename from openspec/changes/sdlc-rename/specs/migration/spec.md rename to openspec/changes/archive/2026-10-24-sdlc-rename/specs/migration/spec.md diff --git a/openspec/changes/sdlc-rename/specs/web-app/spec.md b/openspec/changes/archive/2026-10-24-sdlc-rename/specs/web-app/spec.md similarity index 100% rename from openspec/changes/sdlc-rename/specs/web-app/spec.md rename to openspec/changes/archive/2026-10-24-sdlc-rename/specs/web-app/spec.md diff --git a/openspec/changes/sdlc-rename/specs/wiki/spec.md b/openspec/changes/archive/2026-10-24-sdlc-rename/specs/wiki/spec.md similarity index 100% rename from openspec/changes/sdlc-rename/specs/wiki/spec.md rename to openspec/changes/archive/2026-10-24-sdlc-rename/specs/wiki/spec.md diff --git a/openspec/specs/data-model/spec.md b/openspec/specs/data-model/spec.md index ecb9b5d..d16772f 100644 --- a/openspec/specs/data-model/spec.md +++ b/openspec/specs/data-model/spec.md @@ -195,7 +195,7 @@ SHALL обнулить `issues_count` и `links_count` её `sync_run` (`started передачи `board_id` агенту как запасной доски для команд без доски в параметрах (поставленных до миграции); `GET /settings` SHALL отдавать вычисленную `base_jql` только для чтения, а запись `base_jql` или `board_id` через `PUT /settings` — 422 с пояснением. Спринты агент читает с досок проектов, отдельной настройки имён спринтов нет. Начальные значения SHALL импортироваться из `admin/jira-sync/config.example.json` -командой `jira-hub init`; пароль и логин Jira в настройках MUST NOT присутствовать. `issue_types` — объект +командой `sdlc-hub init`; пароль и логин Jira в настройках MUST NOT присутствовать. `issue_types` — объект `{task, epic, bug}`, у каждого `{name, id}` (`id` может быть `null`); `init` берёт их из ключей конфига `issue_type_<тип>` и `issue_type_<тип>_id`; `bug` попадает в `issue_types`, только если в конфиге есть хотя бы один из ключей `issue_type_bug`, `issue_type_bug_id` (`task` и `epic` — как раньше, всегда). @@ -205,7 +205,7 @@ SHALL обнулить `issues_count` и `links_count` её `sync_run` (`started - **THEN** `settings_version` увеличен, следующий `claim` отдаёт агенту новую версию #### Scenario: Конфиг без ключей ошибки -- **WHEN** `jira-hub init --config` с `issue_type_task`, `issue_type_epic`, без `issue_type_bug` +- **WHEN** `sdlc-hub init --config` с `issue_type_task`, `issue_type_epic`, без `issue_type_bug` - **THEN** `issue_types` содержит `task` и `epic`, `bug` нет; постановка `ensure_issue` с `issue_type = bug` — 422 #### Scenario: Неверный relay_dir diff --git a/openspec/specs/hub-api/spec.md b/openspec/specs/hub-api/spec.md index d2a37e5..f06becc 100644 --- a/openspec/specs/hub-api/spec.md +++ b/openspec/specs/hub-api/spec.md @@ -1,16 +1,16 @@ # hub-api Specification ## Purpose -HTTP API хаба `jira-hub serve`: маршруты агента (выдача команд, отчёты, загрузка состояния), маршруты интерфейсов (зеркало, заметки, пакеты, настройки), резервные копии, автообновление зеркала и реле общей папки для агента за VPN. +HTTP API хаба `sdlc-hub serve`: маршруты агента (выдача команд, отчёты, загрузка состояния), маршруты интерфейсов (зеркало, заметки, пакеты, настройки), резервные копии, автообновление зеркала и реле общей папки для агента за VPN. ## Requirements ### Requirement: Процесс и доступ -`jira-hub serve` SHALL запускать один процесс: HTTP на `0.0.0.0:` (по умолчанию 8790), база и настройки в -`~/.local/share/jira-hub/` (переопределяется `JIRA_HUB_HOME`). Все маршруты API SHALL требовать +`sdlc-hub serve` SHALL запускать один процесс: HTTP на `0.0.0.0:` (по умолчанию 8790), база и настройки в +`~/.local/share/sdlc-hub/` (переопределяется `SDLC_HUB_HOME`). Все маршруты API SHALL требовать `Authorization: Bearer `: токен агента модуля открывает только `/agent/*` (маршруты ядра — от имени этого агента, маршруты модуля со scope `agent` — только агенту своего модуля, см. `modules`, «Агенты модулей»), клиентский — всё -остальное. `jira-hub tokens` SHALL печатать клиентский токен и токены агентов с именами модулей. Исключение — +остальное. `sdlc-hub tokens` SHALL печатать клиентский токен и токены агентов с именами модулей. Исключение — раздача веб-приложения (`GET /app`, `GET /app/…`) и перенаправления `GET /`, `GET /links`: это статические файлы без данных, они отдаются без токена. Ответы API — JSON, ошибки — `{error, detail}` с кодами 400 (неверное тело или `Content-Length`), 401, 404, 409, 413 (тело больше предела), 422, 500 (внутренняя ошибка); реле общей папки @@ -30,7 +30,7 @@ JSON. - **THEN** 200, `Content-Type: text/html; charset=utf-8`, тело — `index.html` сборки; данных зеркала в нём нет #### Scenario: Токены после миграции -- **WHEN** база v6 с агентским токеном T мигрировала в v7, запущен `jira-hub tokens` +- **WHEN** база v6 с агентским токеном T мигрировала в v7, запущен `sdlc-hub tokens` - **THEN** вывод содержит клиентский токен, `jira: T` и новый токен `wiki` ### Requirement: Маршруты агента @@ -249,7 +249,7 @@ SHALL скопировать файл базы в `backups/<дата-время> - **THEN** поставлен один `refresh` с JQL `project = AURORA AND labels in ("AWPCA", "STOCKS_DRRK")` ### Requirement: Реле через общую папку -`jira-hub serve` SHALL запускать поток реле вместе с HTTP-сервером. Пока настройка `relay_dir` пуста (`null`), реле +`sdlc-hub serve` SHALL запускать поток реле вместе с HTTP-сервером. Пока настройка `relay_dir` пуста (`null`), реле бездействует; настройку оно SHALL перечитывать не реже раза в 5 с, включение и выключение не требуют перезапуска. Если каталог `relay_dir` существует, реле SHALL создать в нём подкаталоги `req/`, `resp/`, `work/`; если не существует — записать одно предупреждение в лог и проверять снова. @@ -340,10 +340,10 @@ SHALL скопировать файл базы в `backups/<дата-время> - **THEN** 401 ### Requirement: Публикация версии агента -`jira-hub agent publish [--module <модуль>] PATH` SHALL публиковать файл агента модуля (по умолчанию `jira`; +`sdlc-hub agent publish [--module <модуль>] PATH` SHALL публиковать файл агента модуля (по умолчанию `jira`; неизвестный модуль или модуль без команд — код 2 и список модулей с агентами в stderr): текст в UTF-8 не больше 1 МБ, ровно одна строка `AGENT_VERSION = <целое>`, версия больше опубликованной для этого модуля. Файл SHALL записываться в -`/release/<модуль>/agent.py` (каталоги 0700), затем `manifest.json` `{version, sha256, size, +`/release/<модуль>/agent.py` (каталоги 0700), затем `manifest.json` `{version, sha256, size, published_at}`; оба — через временный файл с `fsync` и переименование, манифест последним. Прежний `agent.py` на время публикации SHALL переименовываться в `agent.py.prev` и удаляться после записи манифеста; если запись нового файла или манифеста не удалась (`OSError`), хаб SHALL вернуть `agent.py.prev` на место `agent.py`, удалить временные файлы и @@ -363,17 +363,17 @@ published_at}` или `null`), `update_state`, `launcher_version`, `agent_report (`rolled_back`), «v отвергнута: » (`rejected`), иначе `null`. #### Scenario: Публикация новой версии -- **WHEN** опубликована v5, `jira-hub agent publish jira-agent/src/agent.py` с `AGENT_VERSION = 6` +- **WHEN** опубликована v5, `sdlc-hub agent publish jira-agent/src/agent.py` с `AGENT_VERSION = 6` - **THEN** в `release/jira/` — новый `agent.py` и манифест с `version = 6`, `agent.py.prev` нет; вывод «опубликована v6, sha256 …»; код 0 #### Scenario: Публикация eva-agent -- **WHEN** `jira-hub agent publish --module wiki eva-agent/src/agent.py` с `AGENT_VERSION = 1`, опубликована jira v14 +- **WHEN** `sdlc-hub agent publish --module wiki eva-agent/src/agent.py` с `AGENT_VERSION = 1`, опубликована jira v14 - **THEN** в `release/wiki/` — v1; `claim` агента `wiki` получает `agent_release {version: 1, …}`, `claim` агента `jira` — `{version: 14, …}` #### Scenario: Неизвестный модуль -- **WHEN** `jira-hub agent publish --module nope agent.py` +- **WHEN** `sdlc-hub agent publish --module nope agent.py` - **THEN** код 2, в stderr «неизвестный модуль nope» и список модулей с агентами; `release/` не изменён #### Scenario: Версия не выше опубликованной @@ -407,7 +407,7 @@ published_at}` или `null`), `update_state`, `launcher_version`, `agent_report - **THEN** публикация v14 лежит в `release/jira/`, `claim` агента `jira` получает `agent_release {version: 14, …}` ### Requirement: Раздача веб-приложения -Хаб SHALL отдавать собранное веб-приложение (спека `web-app`) из каталога пакета `src/jira_hub/web/app/`, читая файлы +Хаб SHALL отдавать собранное веб-приложение (спека `web-app`) из каталога пакета `src/sdlc_hub/web/app/`, читая файлы с диска на каждый запрос: - `GET /app/assets/<путь>` — файл из `assets/` сборки; путь, который после разрешения выходит за `assets/`, не существует или содержит нулевой байт, — 404; `Content-Type` по расширению (`.js` — `text/javascript; charset=utf-8`, `.css` — @@ -416,7 +416,7 @@ published_at}` или `null`), `update_state`, `launcher_version`, `agent_report `Cache-Control: public, max-age=31536000, immutable`; - любой другой `GET /app` или `GET /app/…` — `index.html` сборки, `text/html; charset=utf-8`, `Cache-Control: no-cache`; - если `index.html` сборки нет — на любой `GET /app…` 503 `text/html; charset=utf-8` с текстом, что приложение не - собрано и нужно выполнить `make web` в каталоге jira-hub; + собрано и нужно выполнить `make web` в каталоге sdlc-hub; - `GET /` и `GET /links` — 302 с `Location: /app/board`. Сборка приложения не хранится в git; её создаёт `make web`. @@ -665,3 +665,52 @@ from_string, to, to_string}`. До записи хаб SHALL проверить #### Scenario: Два вложения с одним именем - **WHEN** у задачи два вложения «скрин.png» (#10 от 14.10, #20 от 20.10); комментарий — «!скрин.png!» - **THEN** у комментария `attachment_ids = ["20"]` + +### Requirement: Каталог данных прежнего имени +Если `SDLC_HUB_HOME` не задан, `~/.local/share/sdlc-hub/` не существует, а `~/.local/share/jira-hub/` существует, любая +команда `sdlc-hub`, которой нужен каталог данных (`serve`, `mcp`, `tokens`, `init`, `import`, `agent publish`), SHALL +завершиться с кодом 2 и текстом в stderr: каталог данных лежит под прежним именем, перенести его — +`scripts/cutover.sh` (или `mv ~/.local/share/jira-hub ~/.local/share/sdlc-hub` при остановленном хабе). Команда MUST NOT +создавать новый каталог, переносить или менять прежний. Если существуют оба каталога, используется новый, в stderr — +предупреждение, что прежний можно удалить после проверки. + +#### Scenario: Код обновлён, каталог не перенесён +- **WHEN** есть только `~/.local/share/jira-hub/`, запущен `sdlc-hub serve` +- **THEN** код выхода 2, в stderr — подсказка о переносе; `~/.local/share/sdlc-hub/` не создан, прежний каталог не тронут + +#### Scenario: Задан SDLC_HUB_HOME +- **WHEN** `SDLC_HUB_HOME=/tmp/x`, есть `~/.local/share/jira-hub/` +- **THEN** команда работает с `/tmp/x`, проверка прежнего каталога не выполняется + +### Requirement: Переключение живого хаба +`scripts/cutover.sh` SHALL переводить живую установку Mac mini со старого имени на новое. Без `--apply` скрипт SHALL +только печатать шаги и найденные проблемы, ничего не меняя. С `--apply` — по порядку, с остановкой на первой ошибке: +1. проверки: основной checkout `jira-bridge/jira-hub` чистый, на `main` и совпадает с `origin/main` (владелец сделал + `git pull --ff-only` прямо перед запуском: скрипт приходит этим pull; неотслеживаемая прежняя сборка `src/jira_hub/` + без tracked-файлов не считается грязью); в Gitea доступен `aep/sdlc-hub` (владелец переименовал репозиторий; адрес + выводится из `origin` заменой `/aep/jira-hub` на `/aep/sdlc-hub`, протокол сохраняется, `CUTOVER_REMOTE` + переопределяет); есть `uv`, `make`, `node`, `npm`, `claude`, `launchctl`; `~/.local/share/sdlc-hub/` и `jira-bridge/sdlc-hub` не существуют; +2. остановить launchd `ru.aepif.jira-hub`, дождаться закрытия порта и переименовать его plist в `*.pre-sdlc`; +3. перенести каталог данных `~/.local/share/jira-hub` → `~/.local/share/sdlc-hub`; +4. перенести checkout `jira-bridge/jira-hub` → `jira-bridge/sdlc-hub`, удалить неотслеживаемый `src/jira_hub`, + `git worktree repair`, `origin` → `aep/sdlc-hub`, пересоздать `.venv` (`uv sync`), `make web`; +5. поставить и запустить launchd `ru.aepif.sdlc-hub` (`uv run --project sdlc-hub serve`, лог + `~/Library/Logs/sdlc-hub.log`) и дождаться ответа хаба на порту из настроек; +6. в проектах-потребителях (по умолчанию `~/projects/awp-ca`, `~/projects/stocks_drrk` и каждый + `~/projects/*/.mcp.json`, где есть `jira-hub`, например `sfm_foxpro`; сухой прогон печатает полный список): в `.mcp.json` заменить записи + `jira-hub` → `sdlc-jira` (`sdlc-hub mcp jira`) и `jira-hub-wiki` → `sdlc-wiki` (`sdlc-hub mcp wiki`) с новым путём; в + `.claude/settings*.json` — имена серверов (`enabledMcpjsonServers` и т. п.) и разрешения `mcp__jira-hub__` → + `mcp__sdlc-jira__`, `mcp__jira-hub-wiki__` → `mcp__sdlc-wiki__`; в `CLAUDE.md` и `.claude/skills/**` — те же имена + серверов и инструментов. Прочие файлы проектов (заметки, отчёты) не трогаются. Перед правкой файл копируется рядом + с суффиксом `.pre-sdlc`; коммиты скрипт не делает; +7. в user scope Claude Code заменить `jira-hub` на `sdlc-jira` (`claude mcp remove`/`add`). +Каждый выполненный шаг SHALL печататься; в конце — сводка, что осталось владельцу (`/mcp` reconnect в открытых сессиях, +`codegraph init` в checkout и worktree, удалить копии `.pre-sdlc` после проверки). Шаги откатываются в обратном порядке по инструкции из README. + +#### Scenario: Сухой прогон +- **WHEN** `bash scripts/cutover.sh` без аргументов +- **THEN** напечатаны шаги и проверки, файловая система, launchd и конфигурации Claude не изменены + +#### Scenario: Репозиторий в Gitea не переименован +- **WHEN** `bash scripts/cutover.sh --apply`, `aep/sdlc-hub` в Gitea не найден +- **THEN** скрипт останавливается на шаге 1 с подсказкой, launchd и каталоги не тронуты diff --git a/openspec/specs/mcp/spec.md b/openspec/specs/mcp/spec.md index 018a6d9..ba132cd 100644 --- a/openspec/specs/mcp/spec.md +++ b/openspec/specs/mcp/spec.md @@ -1,30 +1,35 @@ # mcp Specification ## Purpose -MCP-сервер хаба (`jira-hub mcp`, stdio) для Claude: инструменты чтения зеркала и заметок, постановки и отслеживания пакетов команд, обновления зеркала, статуса агента и настроек — поверх HTTP API хаба с клиентским токеном. +MCP-серверы модулей хаба (`sdlc-hub mcp <модуль>`, stdio) для Claude: инструменты чтения зеркала и заметок, постановки и отслеживания пакетов команд, обновления зеркала, статуса агента и настроек — поверх HTTP API хаба с клиентским токеном. ## Requirements ### Requirement: Сервер и регистрация -`jira-hub mcp [<модуль>] [--project <метка>]` SHALL запускать MCP-сервер модуля хаба по stdio (пакет `mcp`), ходящий в -HTTP API хаба клиентским токеном из настроек. Модуль по умолчанию — `jira`: `jira-hub mcp [--project <метка>]` и -`jira-hub mcp jira [--project <метка>]` SHALL поднимать один и тот же сервер с именем `jira-hub` и одним и тем же набором -инструментов. Неизвестный модуль — код выхода 2 и список модулей в stderr, сервер не запускается. Общая регистрация — в -user scope Claude Code как `jira-hub` (`uv run --project <путь> jira-hub mcp`), без проекта. Репозиторий проекта SHALL -регистрировать `jira-hub` в своём `.mcp.json` (project scope) с `--project <метка>`; эта запись перекрывает общую. Если хаб -недоступен, каждый инструмент SHALL вернуть текст «хаб не запущен: jira-hub serve», а не исключение. +`sdlc-hub mcp <модуль> [--project <метка>]` SHALL запускать MCP-сервер модуля хаба по stdio (пакет `mcp`), ходящий в +HTTP API хаба клиентским токеном из настроек; сервер называется `sdlc-<модуль>` (`sdlc-jira`, `sdlc-wiki`). Модуль +обязателен: без него — код выхода 2 и список модулей в stderr, сервер не запускается; неизвестный модуль — так же. +Общая регистрация — в user scope Claude Code как `sdlc-jira` (`uv run --project <путь> sdlc-hub mcp jira`), без +проекта. Репозиторий проекта SHALL регистрировать серверы модулей в своём `.mcp.json` (project scope) с +`--project <метка>`; эта запись перекрывает общую. Если хаб недоступен, каждый инструмент SHALL вернуть текст «хаб не +запущен: sdlc-hub serve», а не исключение. #### Scenario: Хаб выключен - **WHEN** вызван `issues` при остановленном хабе - **THEN** ответ инструмента — одна строка с подсказкой, сервер MCP продолжает работать +#### Scenario: Сервер модуля jira +- **WHEN** запущен `sdlc-hub mcp jira --project AWPCA` +- **THEN** сервер называется `sdlc-jira`, список инструментов и их описания совпадают с прежним сервером `jira-hub`, + сессия привязана к `AWPCA` + #### Scenario: Модуль по умолчанию -- **WHEN** запущены `jira-hub mcp --project AWPCA` и `jira-hub mcp jira --project AWPCA` -- **THEN** оба сервера называются `jira-hub`, список инструментов и их описания совпадают, сессия привязана к `AWPCA` +- **WHEN** запущен `sdlc-hub mcp --project AWPCA` +- **THEN** модуля по умолчанию больше нет: код выхода 2, в stderr — подсказка указать модуль и список модулей (`jira`, `wiki`) #### Scenario: Неизвестный модуль -- **WHEN** запущен `jira-hub mcp nope` -- **THEN** код выхода 2, в stderr — «неизвестный модуль nope» и список модулей (`jira`) +- **WHEN** запущен `sdlc-hub mcp nope` +- **THEN** код выхода 2, в stderr — «неизвестный модуль nope» и список модулей (`jira`, `wiki`) ### Requirement: Инструменты | Инструмент | Параметры | Результат | @@ -61,7 +66,7 @@ user scope Claude Code как `jira-hub` (`uv run --project <путь> jira-hub других проектов запрещены, а команды над задачами вне проекта отклоняются; `board_id` для `ensure_sprint` выставляет хаб. Ответ `project_create` SHALL содержать фрагмент `.mcp.json` -`{"mcpServers": {"jira-hub": {"command": "uv", "args": ["run", "--project", "<корень хаба>", "jira-hub", "mcp", +`{"mcpServers": {"sdlc-jira": {"command": "uv", "args": ["run", "--project", "<корень хаба>", "sdlc-hub", "mcp", "jira", "--project", "<метка>"]}}}`, где корень хаба — корень репозитория, из которого загружен пакет текущего процесса MCP (вычисляется от расположения пакета, без зашитого пути), и указание: положить файл в корень репозитория проекта (слить с имеющимся `.mcp.json`) и переподключить MCP (`/mcp` → reconnect, одобрить сервер проекта). @@ -121,7 +126,7 @@ user scope Claude Code как `jira-hub` (`uv run --project <путь> jira-hub - **THEN** ответ «у AURORA-9001 нет комментария #999» ### Requirement: Привязка сессии к проекту -`jira-hub mcp --project <метка>` SHALL привязывать сервер MCP к проекту. Привязка SHALL проверяться при каждом вызове +`sdlc-hub mcp <модуль> --project <метка>` SHALL привязывать сервер MCP к проекту. Привязка SHALL проверяться при каждом вызове инструмента по `GET /projects`: метки нет — ответ «проект <метка> не заведён в хабе: project_create или исправьте --project в .mcp.json» (кроме `projects`, `project_create`). @@ -226,7 +231,7 @@ user scope Claude Code как `jira-hub` (`uv run --project <путь> jira-hub - **WHEN** `comment {key: "AURORA-1650", comment_id: "101"}` показал «вложения: #31337 скрин 1.png (234,4 КБ)»; вызван `attachment {key: "AURORA-1650", attachment: "#31337"}`, агент онлайн, файла у хаба нет - **THEN** поставлена `fetch_attachment`; через несколько секунд ответ — путь - `…/jira-hub/downloads/31337/скрин 1.png` и строка «скрин 1.png (234,4 КБ), image/png, задача AURORA-1650»; файл по пути + `…/sdlc-hub/downloads/31337/скрин 1.png` и строка «скрин 1.png (234,4 КБ), image/png, задача AURORA-1650»; файл по пути читается #### Scenario: Повторный вызов @@ -249,14 +254,14 @@ user scope Claude Code как `jira-hub` (`uv run --project <путь> jira-hub комментария 101 строка «файлы: #31337 скрин 1.png» ### Requirement: MCP вики -`jira-hub mcp wiki [--project <метка>]` SHALL запускать по stdio MCP-сервер модуля `wiki` с именем `jira-hub-wiki`, -ходящий в HTTP API хаба клиентским токеном; хаб недоступен — каждый инструмент отвечает «хаб не запущен: jira-hub serve». +`sdlc-hub mcp wiki [--project <метка>]` SHALL запускать по stdio MCP-сервер модуля `wiki` с именем `sdlc-wiki`, +ходящий в HTTP API хаба клиентским токеном; хаб недоступен — каждый инструмент отвечает «хаб не запущен: sdlc-hub serve». Привязка к проекту проверяется так же, как у сервера jira («Привязка сессии к проекту»). Инструменты: | Инструмент | Параметры | Ответ | |---|---|---| | `projects` | — | таблица: метка, название, корень вики, страниц, последняя синхронизация; текущий проект отмечен | -| `project_create` | `label`, `name`, `wiki_root` (необязательно) | `POST /projects` с автором `claude`; проект и фрагмент `.mcp.json` с записью `jira-hub-wiki` | +| `project_create` | `label`, `name`, `wiki_root` (необязательно) | `POST /projects` с автором `claude`; проект и фрагмент `.mcp.json` с записью `sdlc-wiki` | | `root_set` | `root` | `PATCH /projects/<текущий> {wiki_root}`; новый корень и id пакета синхронизации | | `tree` | `code` (необязательно), `depth` (по умолчанию 2) | дерево от корня или от `code`: отступ, код, название, размер | | `page` | `code` или `title`, `offset` (по умолчанию 0) | шапка (код, путь в дереве, адрес в Eva, дата синхронизации, вложения с `id`) и Markdown с `offset` до 20 000 символов, в конце — «продолжение: offset=», если текст длиннее; `title` ищется точно, иначе — до 10 кандидатов с кодами | @@ -267,7 +272,7 @@ user scope Claude Code как `jira-hub` (`uv run --project <путь> jira-hub Инструменты, кроме `projects` и `project_create`, SHALL требовать привязки к проекту. В привязанной сессии без корня (кроме `root_set`, `status`) ответ SHALL быть «у проекта <метка> нет корня вики: root_set DOC-…»; с корнем, но без -синхронизаций — «вики проекта ещё не синхронизирована: sync». Набор инструментов и их описания у сервера `jira-hub` +синхронизаций — «вики проекта ещё не синхронизирована: sync». Набор инструментов и их описания у сервера `sdlc-jira` (модуль jira) SHALL остаться прежним. #### Scenario: Поиск из сессии awp-ca @@ -284,5 +289,5 @@ user scope Claude Code как `jira-hub` (`uv run --project <путь> jira-hub - **THEN** ответ «у проекта sfm_kedr нет корня вики: root_set DOC-…», запроса `GET /wiki/tree` нет #### Scenario: Сервер jira не изменился -- **WHEN** запущен `jira-hub mcp --project AWPCA` -- **THEN** имя сервера `jira-hub`, список инструментов и описания совпадают с эталоном до change +- **WHEN** запущен `sdlc-hub mcp jira --project AWPCA` +- **THEN** имя сервера `sdlc-jira`, список инструментов и описания совпадают с эталоном до change diff --git a/openspec/specs/migration/spec.md b/openspec/specs/migration/spec.md index 3aa7b78..b2344d2 100644 --- a/openspec/specs/migration/spec.md +++ b/openspec/specs/migration/spec.md @@ -6,7 +6,7 @@ ## Requirements ### Requirement: Инициализация настроек -`jira-hub init --config ` SHALL перенести в `setting`: `project_key`, типы задач, +`sdlc-hub init --config ` SHALL перенести в `setting`: `project_key`, типы задач, `epic_name_field_id`, `fields`, `epic_link`, `priority_map`, `labels`, `status_labels`, `kind_labels`, `comment_marker`, `pacing` (в новую форму по типам команд), `transitions.map`, `link_types`; сгенерировать два токена; завести проект `AWPCA` «АРМ КА» с `board_id` из `sprints.board_id` конфига (если проекта `AWPCA` ещё нет). Поля `auth`, `base_url`, @@ -21,7 +21,7 @@ - **THEN** есть проект `AWPCA` «АРМ КА» с `board_id = 4216`; в `setting` нет `base_jql` и `board_id` ### Requirement: Импорт CSV-пакета -`jira-hub import <папка admin/jira-sync>` SHALL прочитать `tasks.csv`, `new_tasks.csv`, `epics.csv` и заполнить +`sdlc-hub import <папка admin/jira-sync>` SHALL прочитать `tasks.csv`, `new_tasks.csv`, `epics.csv` и заполнить `issue_note` по ключу Jira: статус по факту, вид работы, что сделано, что осталось, внутренние колонки. Строки `new_tasks.csv` без ключа SHALL получать временный ключ `new:<номер строки>`; строки `tasks.csv` и `epics.csv` без ключа SHALL пропускаться. `links.csv` SHALL импортироваться как сверочный набор в отчёт импорта (ожидаемые связи против зеркала diff --git a/openspec/specs/web-app/spec.md b/openspec/specs/web-app/spec.md index 3e78a0b..cdb18a0 100644 --- a/openspec/specs/web-app/spec.md +++ b/openspec/specs/web-app/spec.md @@ -1,20 +1,24 @@ # web-app Specification ## Purpose -Веб-приложение хаба (`web/`, React + TypeScript + Vite) под префиксом `/app/`: вход по клиентскому токену, доска связей (перенос `links.html`) и страницы задач со служебными заметками; сборка в `src/jira_hub/web/app/`. +Веб-приложение хаба (`web/`, React + TypeScript + Vite) под префиксом `/app/`: вход по клиентскому токену, доска связей (перенос `links.html`) и страницы задач со служебными заметками; сборка в `src/sdlc_hub/web/app/`. ## Requirements ### Requirement: Вход по токену Приложение SHALL работать с API хаба того же хоста под клиентским токеном: хранить его в `localStorage` под ключом -`jira-hub-token` (и в памяти, если хранилище закрыто) и передавать в заголовке `Authorization: Bearer` каждого запроса. +`sdlc-hub-token` (и в памяти, если хранилище закрыто) и передавать в заголовке `Authorization: Bearer` каждого запроса. Если токена нет или любой запрос ответил 401, приложение SHALL показывать экран входа вместо страниц: поле токена с -подсказкой `uv run jira-hub tokens`, без панелей страниц и без фоновых запросов, которые могли бы изменить поле ввода. +подсказкой `uv run sdlc-hub tokens`, без панелей страниц и без фоновых запросов, которые могли бы изменить поле ввода. Введённый токен SHALL проверяться запросом `GET /settings` и сохраняться только при успехе. Действие «Сменить токен» SHALL очищать токен и показывать экран входа. Ответ 401 SHALL выводить на экран входа, только если запрос ушёл с текущим токеном: запоздавший 401 на запрос с прежним токеном после входа с новым не учитывается. Если проверка токена получила ответ 4xx, кроме 401, экран входа SHALL показывать текст ошибки хаба, а не «хаб не отвечает». +Ключи `localStorage` приложения с префиксом `sdlc-hub-` (`sdlc-hub-token`, `sdlc-hub-sidebar`, `sdlc-hub-legend`) SHALL +при первом чтении, если ключа нет, брать значение одноимённого ключа с префиксом `jira-hub-`, записывать его под новым +ключом и удалять прежний. + #### Scenario: Первый вход - **WHEN** приложение открыто впервые, токена нет - **THEN** показан экран входа; после ввода верного токена открывается запрошенная страница, токен сохранён @@ -24,8 +28,8 @@ SHALL очищать токен и показывать экран входа. - **THEN** показан экран входа с пояснением, что токен не подошёл; панели доски и задач не видны #### Scenario: Токен от прежней доски -- **WHEN** в `localStorage` есть `jira-hub-token`, сохранённый страницей `links.html` -- **THEN** приложение входит с ним без запроса токена +- **WHEN** в `localStorage` нет `sdlc-hub-token`, но есть `jira-hub-token` (сохранён до переименования хаба) +- **THEN** приложение входит с ним без запроса токена; значение перенесено в `sdlc-hub-token`, `jira-hub-token` удалён #### Scenario: Запоздавший 401 от прежнего токена - **WHEN** запрос ушёл с прежним токеном, пользователь вошёл с новым, и после этого запрос ответил 401 @@ -40,13 +44,13 @@ SHALL очищать токен и показывать экран входа. задач), `/app/feed` (лента изменений, требование «Лента изменений») и `/app/issues/{key}` (карточка задачи — окно поверх страницы, требование «Карточка задачи»); `/app` и `/app/` SHALL вести на `/app/board`, неизвестный путь — на страницу «не найдено» со ссылками на разделы. Боковая панель SHALL быть закреплена у левого края окна на всю его высоту и MUST NOT прокручиваться вместе со страницей; страница занимает остальную ширину. Сверху вниз панель SHALL -содержать: название «Хаб Jira» и кнопку «Свернуть панель»; выпадающий список проектов («<метка> — <название>», текущий +содержать: название «SDLC Hub» и кнопку «Свернуть панель»; выпадающий список проектов («<метка> — <название>», текущий отмечен, в конце «+ Новый проект…»; требование «Проект в приложении»); разделы «Доска связей», «Задачи» и «Лента» — вертикальным списком ссылок со значками (текущий отмечен `aria-current="page"`); внизу — блок данных Jira с действием «Обновить из Jira» (требование «Обновление из Jira»), переключатель темы (требование «Тема») и меню с действием «Сменить токен». Свёрнутая панель SHALL быть узкой полосой с теми же действиями значками: «Развернуть панель», метка текущего проекта, разделы, «Обновить из Jira» с точкой состояния агента, тема, меню; названия — в подсказках и `aria-label`. Свёртка -SHALL храниться в `localStorage` под ключом `jira-hub-sidebar` (`collapsed`; без записи — развёрнута). На экранах уже +SHALL храниться в `localStorage` под ключом `sdlc-hub-sidebar` (`collapsed`; без записи — развёрнута). На экранах уже 640 px панели в потоке нет: сверху SHALL быть закреплена строка с кнопкой «Меню», названием и кнопкой «Обновить из Jira»; «Меню» открывает развёрнутую панель поверх страницы, панель закрывается выбором раздела, клавишей Escape или щелчком мимо неё. Горизонтальной прокрутки страницы на любой ширине нет. @@ -116,7 +120,7 @@ Jira»; «Меню» открывает развёрнутую панель по управления: мультиселекты «Спринты» и «Исполнители», переключатели группы «Показать» («Соседи по связям», «Только передачи», «Скрыть без связей») и группы «Режим» («Критический путь», «По датам»), каждый с `aria-pressed` и прежней подсказкой. Под панелью — легенда, которая сворачивается кнопкой «Легенда»; свёрнутая показывает число скрытых видов -работ и типов связей; состояние свёртки хранится в `localStorage` под ключом `jira-hub-legend`. «Обновить из Jira», +работ и типов связей; состояние свёртки хранится в `localStorage` под ключом `sdlc-hub-legend`. «Обновить из Jira», тема и «Сменить токен» — в боковой панели приложения, на доске их нет. У карточки задачи (не у заглушки) SHALL быть значок «Открыть карточку», @@ -367,15 +371,15 @@ SHALL сообщать об этом с кнопкой «Закрыть»; ха ### Requirement: Сборка и разработка Приложение SHALL быть npm-проектом в `web/` (React, TypeScript, Vite) с точными версиями зависимостей и `package-lock.json`. `make web` SHALL устанавливать зависимости (`npm ci`) и собирать приложение в -`src/jira_hub/web/app/`; каталог сборки не хранится в git. Шрифты (Mulish — текст интерфейса, IBM Plex Mono — ключи +`src/sdlc_hub/web/app/`; каталог сборки не хранится в git. Шрифты (Mulish — текст интерфейса, IBM Plex Mono — ключи задач и исходники), иконки и прочие ресурсы страницы SHALL входить в сборку: приложение не обращается к внешним адресам. `make check` SHALL запускать `uv run pytest`, проверку типов и линтер фронта и тесты фронта; зелёным считается -только весь набор. `npm run dev` SHALL поднимать сервер разработки с прокси маршрутов API на хаб из `JIRA_HUB_URL` (по +только весь набор. `npm run dev` SHALL поднимать сервер разработки с прокси маршрутов API на хаб из `SDLC_HUB_URL` (по умолчанию тестовый `http://127.0.0.1:8791`). #### Scenario: Сборка с нуля - **WHEN** в чистом checkout выполнен `make web` -- **THEN** в `src/jira_hub/web/app/` есть `index.html` и `assets/`; `git status` не показывает файлов сборки +- **THEN** в `src/sdlc_hub/web/app/` есть `index.html` и `assets/`; `git status` не показывает файлов сборки #### Scenario: Без интернета - **WHEN** приложение открыто на машине без доступа в интернет diff --git a/openspec/specs/wiki/spec.md b/openspec/specs/wiki/spec.md index e43e437..a840377 100644 --- a/openspec/specs/wiki/spec.md +++ b/openspec/specs/wiki/spec.md @@ -81,7 +81,7 @@ links: [{ref, code}]}`, где `attachments` — ссылки и картинк - **THEN** 409 `sync_in_progress` с `batch_id` текущего пакета, нового пакета нет ### Requirement: Markdown-копия вики -Копия проекта SHALL лежать в `/wiki/<метка>/` (каталоги 0700): у каждой страницы — каталог +Копия проекта SHALL лежать в `/wiki/<метка>/` (каталоги 0700): у каждой страницы — каталог `NN_<название>` внутри каталога родителя (`NN` — двузначная позиция среди соседей по `orderno`, затем по коду; у корня номера нет; название без символов `<>:"/\|?*` и управляющих, обрезано до 80 символов; совпадение у соседей — суффикс `_2`, `_3`), в нём `index.md`: frontmatter `code`, `title`, `url`, `synced_at`, строка `# ` и текст. В