Files
agentik/agentik-cli/README.md
T
subochev 65e05612a1
ci / JVM build + tests (push) Failing after 2m0s
release / Publish KMP libraries → caffeine Nexus (release) Successful in 5m35s
refactor(agentik-cli): вложенные subcommands (conv ls/new/...)
- conv-ls/new/show/delete/rename -> вложенные под agentik-cli conv
- ConvCommand — Subcommand-родитель, регистрирует 5 дочерних
  команд в init { subcommands(...) }
- ConvSubcommand(name, description) extends AgentikSubcommand —
  базовый класс для всех conv-подкоманд (наследует --server/--id)

Два гоччаса kotlinx.cli 0.3.6 которые пришлось обойти:

1. parent.execute() вызывается ПОСЛЕ leaf.execute() всегда когда
   leaf достигнут через parent. Если parent делает что-то в
   execute() — вывод дублируется после каждой дочерней команды.
   Фикс: ConvCommand.execute() = Unit (no-op). Дочерние команды
   смотрятся через 'agentik-cli conv --help'.

2. По умолчанию 'conv new --server ...' парсится как
   conv[--server ...] + позиционный arg 'new' на уровне
   родителя, и дочерняя команда не запускается. Фикс:
   ArgParser(strictSubcommandOptionsOrder = true) — все аргументы
   после имени subcommand передаются в его парсер.

Smoke (linuxX64 kexe + JVM fatjar): conv ls/new/rename/show/delete
+ msgs/send/interrupt/info работают.
2026-09-18 00:29:48 +03:00

178 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `:agentik-cli` — one-shot CLI клиент к `/agentik`
## Что это
**One-shot subcommand CLI** (Kotlin Multiplatform) к серверу
`:standalone` через `:client` по HTTP+SSE. Один вызов — одна команда:
стрим ответа `send` идёт в stdout построчно, никакого embedded-REPL.
Решает: быстрый способ дёрнуть агента из shell-скрипта или руками,
не поднимая отдельную TUI-сессии.
## Платформы
| Платформа | Артефакт | Размер | Статус |
|---|---|---|---|
| `jvm` (JRE 21) | `*-all.jar` | ~7 МБ | ✓ собирается и работает |
| `linuxX64` | `.kexe` | ~5 МБ | ✓ собирается и работает на этом хосте |
| `macosX64` | `.kexe` | — | собирается на macOS-раннере |
| `macosArm64` | `.kexe` | — | собирается на macOS-arm64-раннере |
| `mingwX64` | `.exe` | ~6 МБ | ✓ собирается (cross-compile с Linux) |
| `linuxArm64` | — | — | **нет** — kotlinx-cli 0.3.6 не публикует klib для linuxArm64 |
| `iOS` | — | — | нет смысла на iOS |
## Подкоманды
```
agentik-cli <command> [args...]
Команды верхнего уровня:
conv <subcommand> операции над диалогами (см. ниже)
msgs <id> [--limit N] показать сообщения
send <id> <text...> отправить ход, стримит response-события в stdout
interrupt <id> прервать текущий ход
info показать конфиг (server URL + agent id)
Подкоманды `conv`:
conv ls список диалогов
conv new [--temp] создать диалог, печатает id
conv show <id> метаданные диалога
conv delete <id> удалить диалог
conv rename <id> <title> переименовать
```
`--server URL` и `--id ID` (env: `AGENTIK_SERVER`, `AGENTIK_AGENT_ID`)
задаются **после** имени subcommand'а — kotlinx.cli не шарит опции
родителя в subcommand. Примеры:
```bash
agentik-cli conv ls --server http://192.168.76.166:8080/agentik
agentik-cli conv new --server http://localhost:8080/agentik
agentik-cli send --server http://localhost:8080/agentik conv-abc "привет"
agentik-cli info # через AGENTIK_SERVER env-переменную
```
## Как запустить
### JVM (fatjar)
```bash
./gradlew :agentik-cli:shadowJar
java --enable-native-access=ALL-UNNAMED \
-jar agentik-cli/build/libs/agentik-cli-0.1.0-SNAPSHOT-all.jar conv --help
```
### Native linuxX64
```bash
./gradlew :agentik-cli:linkReleaseExecutableLinuxX64
./agentik-cli/build/bin/linuxX64/releaseExecutable/agentik-cli.kexe conv --help
```
### Native macOS / Windows
На Linux-хосте `macosX64`/`macosArm64` линкуются пустыми (нужен
macOS-раннер, Apple Mach-O формат). `mingwX64` собирается через
кросс-компиляцию.
CI-ноут: запускать `./gradlew :agentik-cli:linkReleaseExecutableMacosX64
:agentik-cli:linkReleaseExecutableMacosArm64` на `macos-latest`
раннере Gitea Actions.
## Примеры
```bash
# Список диалогов (таблица)
agentik-cli conv ls --server http://localhost:8080/agentik
# Создать диалог
ID=$(agentik-cli conv new --server http://localhost:8080/agentik)
echo "new conv: $ID"
# Переименовать
agentik-cli conv rename --server http://localhost:8080/agentik "$ID" "мой чат"
# Отправить ход и стримить ответ
agentik-cli send --server http://localhost:8080/agentik "$ID" "2+2"
# Показать последние N сообщений
agentik-cli msgs --server http://localhost:8080/agentik "$ID" --limit 10
# Прервать активный ход
agentik-cli interrupt --server http://localhost:8080/agentik "$ID"
# Удалить
agentik-cli conv delete --server http://localhost:8080/agentik "$ID"
# Через env-переменную
AGENTIK_SERVER=http://localhost:8080/agentik agentik-cli info
```
## Формат вывода `send`
Каждое SSE-событие печатается отдельной строкой `event <Type> ...` —
пригодно для парсинга через `awk`/`jq`-обёртки:
```
event StartReasoning
event StartResponse TEXT
event AppendText \n\n
event AppendText Привет!
event End
```
Терминальные события (`End`, `Interrupted`, `Error`) тоже
печатаются; CLI выходит сразу после `End`.
## Почему kotlinx.cli (а не clikt)
- **kotlinx.cli 0.3.6** (JetBrains, KMP) — единственный зрелый
arg-parser, который стабильно линкуется под `linux_x64` +
`macos_x64`/`macos_arm64` + `mingw_x64`. Минус: нет `linux_arm64`.
- **clikt-multiplatform 5.x** (ajalt) — имеет linuxArm64, но
ломается на native linker: `duplicate symbol selfAndAncestors`
между `clikt` и `clikt-mordant` commonMain (issue
[ajalt/clikt#598](https://github.com/ajalt/clikt/issues/598)).
Workaround `kotlin.native.cacheKind.linuxX64=none` замедляет
сборку на порядки и не решает проблему до конца. Поэтому clikt
отвергнут.
## Платформенные детали
- **entryPoint на K/N** — это FQN функции **без** суффикса `Kt`
(т.е. `pw.binom.agentik.cli.main`, а не `MainKt.main`). JVM
convention `MainKt.main` тут не работает — K/N линкер ищет
функцию по `package.main`.
- **`platformEnv(key)`** для чтения env-переменных:
- JVM: `System.getenv(key)` через `jvmMain` actual.
- Native: `getenv(key)` из `platform.posix` через
`kotlinx.cinterop.toKString()` (`nativeMain` actual,
требует `@OptIn(ExperimentalForeignApi::class)`).
- **Stdout / exit code** — работают на K/N через корутины.
## Готчасы kotlinx.cli
- **Вложенные subcommands + parent.execute().** В kotlinx.cli 0.3.6
`parent.execute()` вызывается ПОСЛЕ `leaf.execute()` всегда,
когда leaf был достигнут через parent. Поэтому `ConvCommand.execute()`
сделан no-op (`override fun execute() = Unit`), иначе вывод
дочерней команды дублируется выводом родителя. Дочерние команды
смотрятся через `agentik-cli conv --help`.
- **strictSubcommandOptionsOrder.** Без этого флага `conv new --server ...`
парсится как `conv [--server ...]` + позиционный аргумент `new`
на уровне родителя — и дочерняя команда не запускается.
В `ArgParser` сразу включается `strictSubcommandOptionsOrder = true`.
## Тесты
Тесты для подкоманд пока не написаны (TODO). Базовый smoke
покрывается руками против живого сервера.
```bash
./gradlew :agentik-cli:jvmTest # 0/0 — пока пусто
```
## Версии
`gradle/libs.versions.toml` → `[versions] agentik-agentik-cli`.