# dsh-plannotator
[English](README.md) | **Русский**
**Ревью файла, папки или веб-страницы в браузерном интерфейсе Plannotator — аннотации возвращаются агенту обычным сообщением в диалоге.**
[](https://github.com/martyartem/dsh-plannotator/releases)
[](LICENSE)
[](#разработка)
[](https://github.com/martyartem/dsh-plannotator/actions/workflows/ci.yml)
[](https://github.com/deepseek-ai/deepseek-harness)
---
## Зачем этот плагин
Попросить агента провести ревью в DSH мешают две вещи, и каждая бьёт по-своему.
| Что мешает | Как это выглядит для вас | Что делает плагин |
|---|---|---|
| **Ревью блокирует вызов** | Сервер ревью ждёт человека минутами, а bash-вызов в DSH отваливается по таймауту куда раньше: процесс уходит в фон, и если его убить, **замечания, которые вы уже расставили, пропадут без следа**. | Запускает ревью в фоне, сразу отдаёт ссылку, а решение доставляет в диалог, когда человек его отправит. |
| **`plannotator` не находится** | Сессия DSH стартует с `PATH=/usr/bin:/bin:/usr/sbin:/sbin`, поэтому установка в `~/.local/bin/plannotator` в него не попадает — агент видит `plannotator: command not found` и ревью просто не запускается. | Ищет CLI сам: конфиг плагина → `PLANNOTATOR_BIN` → `PATH` → известные каталоги установки. |
## Возможности
| Что | Где | Что делает |
|---|---|---|
| Кнопка **Review** | Шапка предпросмотра документа, правая панель | Запускает ревью файла на экране. Файл, который Plannotator не примет (PDF, картинка, Office, экзотическое расширение), уходит на ревью **содержащей папкой** — кнопка никогда не ведёт в тупик. |
| `/plannotator <файл\|папка\|url>` | Композер | То же ревью из строки ввода — и это неблокирующий вход для агента. |
| `/plannotator` | Композер | Ревью рабочей папки текущей сессии. |
| `/plannotate` | Композер | Алиас. |
| Доставка аннотаций | Диалог | Запись решения (`--json`) приезжает сообщением, и агент правит документ по аннотациям в том же чате. |
Это вся поверхность: **одно** место в сайдбаре, **одна** команда и **два** HTTP-маршрута, без флагов.
## Требования
- **DeepSeek Harness** `>=0.1.0-rc.5` (объявлено в `package.json`).
- **Node** `>=20` для хостовой половины. Зависимостей во время работы нет.
- Установленный **Plannotator CLI**, доступный плагину: конфиг плагина, `PLANNOTATOR_BIN`, `PATH`
или один из каталогов `~/.local/bin`, `/usr/local/bin`, `/opt/homebrew/bin`, `~/.bun/bin`,
`~/.cargo/bin`. Без него плагин всё равно загрузится и подскажет, как указать путь к CLI.
## Установка
### Через менеджер плагинов Harness
Откройте **Plugins** в приложении и установите этот репозиторий:
```text
https://github.com/martyartem/dsh-plannotator
```
Затем перезапустите приложение. Имя npm-пакета — `plannotator-dsh`, имя репозитория —
`dsh-plannotator`.
### Из исходников
Клонируйте куда угодно **вне** `~/.dsh` и поставьте ссылкой (`link`), а не копией, — тогда правки
доедут до профиля:
```sh
git clone https://github.com/martyartem/dsh-plannotator ~/Projects/plannotator-dsh
```
Добавьте в профиль (`~/.dsh/profiles/<профиль>/package.json`) и перезапустите приложение:
```json
{
"dependencies": { "plannotator-dsh": "link:/Users/you/Projects/plannotator-dsh" },
"dsh": { "profile": { "bundles": ["…", "plannotator-dsh"] } }
}
```
Откатить установку: `scripts/uninstall.sh [профиль] [--purge]`.
## Конфигурация
Значения задаются в патч-слое профиля (`cordis.patch.yml`) — пользовательский патч-слой переживает
обновления DSH:
```yaml
- id: plannotator-dsh
name: plannotator-dsh
config:
gate: true
timeoutMs: 1800000
```
| Ключ | По умолчанию | Значение |
|---|---|---|
| `binary` | автопоиск | явный путь к CLI `plannotator` |
| `binaryFallbacks` | список известных | дополнительные пути-кандидаты |
| `origin` | `dsh` | значение `PLANNOTATOR_ORIGIN`, которым помечается каждое ревью |
| `gate` | `false` | добавлять кнопку Approve в каждое ревью |
| `openBrowser` | `true` | разрешить Plannotator открывать вкладку браузера |
| `port` | случайный | зафиксировать порт локального сервера ревью |
| `timeoutMs` | `900000` | сколько ревью может висеть открытым, прежде чем его остановят |
| `readyTimeoutMs` | `20000` | сколько ждать готовности локального сервера |
| `autoFollowup` | `true` | доставлять аннотации обратно в диалог |
| `debug` | `false` | дублировать лог плагина в stderr |
| `dataDir` | `~/.dsh/storages/plannotator-dsh` | где лежит `plannotator-dsh.log` |
## Как это устроено
- **Хостовая половина** — `lib/index.js`: регистрирует команду и два маршрута, находит CLI и
рабочую папку, запускает процесс Plannotator в фоне, ждёт файл готовности, затем читает
запись решения из его stdout и доставляет её в сессию.
- **Браузерная половина** — `lib/client.js`: написана вручную в формате модульного загрузчика хоста
(`window.__ModuleLoader__.load`), поэтому **сборщик не нужен** — только `react` из таблицы
платформенных модулей. Место в интерфейсе она вносит через
`ctx.slots.inject(seat, () => ctx.slots.register(…))` — это документированный способ не зависеть
от порядка запуска плагинов.
- **HTTP-маршруты** — браузерная половина не импортирует сервисы хоста:
| Маршрут | Назначение |
|---|---|
| `GET /plannotator/review?target=<путь\|url>[&session=]` | запускает ревью в фоне, отвечает `{ ok, url, label, gate }` |
| `POST /plannotator/log` | пересылает клиентскую заметку в лог плагина |
- **Определение рабочей папки**, по порядку: папка сессии живого агента → `DSH_WORKSPACE_DIR` /
`DSH_CWD` → `ctx.workspaceRegistry` → `cwd` из заголовка лога сессии → самая свежая сессия →
cwd процесса хоста.
- **Контракт доставки** — сообщение, которое получает агент, это обычный `UserMessage` DSH со всеми
обязательными полями: `id`, `role`, `content` и `source.kind` вида `plugin:plannotator-dsh`. Путь
записи принимает сообщение без `id`, но путь восстановления после этого отклоняет **всю сессию**
(`session event at seq N lacks an identified message`), и история перестаёт загружаться. Плагин
штампует `id` сам, а офлайн-набор тестов проверяет его наличие.
## Совместимость
| Зависимость | Диапазон |
|---|---|
| DeepSeek Harness | `>=0.1.0-rc.5` |
| Node | `>=20`, только встроенные модули |
| Plannotator CLI | документированный контракт: argv `annotate --json`, файл готовности (`PLANNOTATOR_READY_FILE`), запись решения в stdout, `PLANNOTATOR_ORIGIN`, `PLANNOTATOR_PORT` |
## Если что-то не работает
Всё пишется в `~/.dsh/storages/plannotator-dsh/plannotator-dsh.log`. После обновления DSH или
Plannotator вся проверка — одна команда:
```sh
grep -E "loaded|apply:|registered|unavailable|steering failed" \
~/.dsh/storages/plannotator-dsh/plannotator-dsh.log | tail -12
```
| Строка | Что означает |
|---|---|
| `loaded …; binary=…` | хост загрузил плагин и нашёл CLI |
| `registered /plannotator/review and /plannotator/log` | HTTP-маршруты на месте |
| `client[info]: document preview action registered` | место кнопки Review существует |
| `document preview action unavailable: …` | место переименовали или убрали |
| `steering failed: …` | хост отклонил доставленное сообщение |
| `session … timed out after …` | ревью превысило `timeoutMs` и было остановлено |
Если первой строки нет — плагин вообще не загрузился, обычно из-за переименованного сервиса,
объявленного в `inject`.
## Разработка
Нужен только Node: ни установки зависимостей, ни сборщика, ни запущенного профиля.
```sh
npm test # обе офлайн-половины
node scripts/selftest.mjs # хостовая половина: 35 проверок — разбор ввода, поиск CLI и папки, сессии, маршруты
node scripts/client-test.mjs # браузерная половина: 12 проверок — форма клиентского модуля, единственное место, гонка холодного старта
node scripts/live-test.mjs # против настоящего CLI Plannotator и его HTTP API
```
```
lib/
index.js хостовая строка: команда, HTTP-маршруты, выбор агента
command.js ввод → argv для Plannotator
review.js ревью в фоне, готовность, решение, доставка, таймаут
config.js значения конфига по умолчанию, файл лога, утилиты
binary.js поиск CLI, не зависящий от PATH
workspace.js относительно какой папки разворачивается цель
client.js браузерная половина: место Review в предпросмотре документа
scripts/ офлайн-наборы, живой смоук-тест, скрипт удаления
```
## Замечания и ограничения
- `annotate` принимает markdown, текстовые конфиги и данные, HTML, URL и папки. `.env` Plannotator
отклоняет сам.
- Git-диффов, pull request'ов и архива здесь **сознательно нет**: их покрывает сам Plannotator CLI,
а слэш-команда, которая висит минутами, — ровно то, ради чего этот плагин и написан.
- Ревью, которое никто не завершил (процесс убили, машина уснула), не даёт решения. Плагин так и
говорит вместо того, чтобы делать вид, будто замечаний не было.
- Плагин не разбирает разметку браузерного интерфейса: он использует только документированный
контракт stdout и файл готовности CLI.
## Лицензия
[MIT](LICENSE)