chore(openspec): архив sdlc-rename, Purpose спек hub-api, mcp, web-app под sdlc-hub (#97)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
93bb968235
commit
b92d5e8582
@ -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
|
||||
|
||||
@ -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:<port>` (по умолчанию 8790), база и настройки в
|
||||
`~/.local/share/jira-hub/` (переопределяется `JIRA_HUB_HOME`). Все маршруты API SHALL требовать
|
||||
`sdlc-hub serve` SHALL запускать один процесс: HTTP на `0.0.0.0:<port>` (по умолчанию 8790), база и настройки в
|
||||
`~/.local/share/sdlc-hub/` (переопределяется `SDLC_HUB_HOME`). Все маршруты API SHALL требовать
|
||||
`Authorization: Bearer <token>`: токен агента модуля открывает только `/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 записываться в
|
||||
`<JIRA_HUB_HOME>/release/<модуль>/agent.py` (каталоги 0700), затем `manifest.json` `{version, sha256, size,
|
||||
`<SDLC_HUB_HOME>/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<to> отвергнута: <error>» (`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 <checkout> 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 и каталоги не тронуты
|
||||
|
||||
@ -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=<N>», если текст длиннее; `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
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
## Requirements
|
||||
|
||||
### Requirement: Инициализация настроек
|
||||
`jira-hub init --config <admin/jira-sync/config.example.json>` SHALL перенести в `setting`: `project_key`, типы задач,
|
||||
`sdlc-hub init --config <admin/jira-sync/config.example.json>` 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 импортироваться как сверочный набор в отчёт импорта (ожидаемые связи против зеркала
|
||||
|
||||
@ -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** приложение открыто на машине без доступа в интернет
|
||||
|
||||
@ -81,7 +81,7 @@ links: [{ref, code}]}`, где `attachments` — ссылки и картинк
|
||||
- **THEN** 409 `sync_in_progress` с `batch_id` текущего пакета, нового пакета нет
|
||||
|
||||
### Requirement: Markdown-копия вики
|
||||
Копия проекта SHALL лежать в `<JIRA_HUB_HOME>/wiki/<метка>/` (каталоги 0700): у каждой страницы — каталог
|
||||
Копия проекта SHALL лежать в `<SDLC_HUB_HOME>/wiki/<метка>/` (каталоги 0700): у каждой страницы — каталог
|
||||
`NN_<название>` внутри каталога родителя (`NN` — двузначная позиция среди соседей по `orderno`, затем по коду; у корня
|
||||
номера нет; название без символов `<>:"/\|?*` и управляющих, обрезано до 80 символов; совпадение у соседей —
|
||||
суффикс `_2`, `_3`), в нём `index.md`: frontmatter `code`, `title`, `url`, `synced_at`, строка `# <title>` и текст. В
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user