Files
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

7.4 KiB
Raw Permalink Blame History

: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. Примеры:

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)

./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

./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.

Примеры

# Список диалогов (таблица)
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). 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 покрывается руками против живого сервера.

./gradlew :agentik-cli:jvmTest   # 0/0 — пока пусто

Версии

gradle/libs.versions.toml → [versions] agentik-agentik-cli.