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 работают.
This commit is contained in:
@@ -5,11 +5,7 @@ import kotlinx.cli.ArgType
|
||||
import kotlinx.cli.ExperimentalCli
|
||||
import kotlinx.cli.Subcommand
|
||||
import kotlinx.cli.default
|
||||
import pw.binom.agentik.cli.commands.ConvDeleteSubcommand
|
||||
import pw.binom.agentik.cli.commands.ConvLsSubcommand
|
||||
import pw.binom.agentik.cli.commands.ConvNewSubcommand
|
||||
import pw.binom.agentik.cli.commands.ConvRenameSubcommand
|
||||
import pw.binom.agentik.cli.commands.ConvShowSubcommand
|
||||
import pw.binom.agentik.cli.commands.ConvCommand
|
||||
import pw.binom.agentik.cli.commands.InfoSubcommand
|
||||
import pw.binom.agentik.cli.commands.InterruptSubcommand
|
||||
import pw.binom.agentik.cli.commands.MsgsSubcommand
|
||||
@@ -32,36 +28,40 @@ internal fun defaultAgentId(): String = platformEnv("AGENTIK_AGENT_ID") ?: "cli"
|
||||
* agentik-cli <command> [args...]
|
||||
*
|
||||
* Commands:
|
||||
* conv-ls список диалогов
|
||||
* conv-new [--temp] создать диалог, печатает id
|
||||
* conv-show <id> метаданные диалога
|
||||
* conv-delete <id> удалить диалог
|
||||
* conv-rename <id> <title> переименовать
|
||||
* msgs <id> [--limit N] показать сообщения
|
||||
* send <id> <text...> отправить ход, стримит response-события в stdout
|
||||
* interrupt <id> прервать текущий ход
|
||||
* info показать конфиг
|
||||
* conv ls|new|show|delete|rename операции над диалогами
|
||||
* msgs <id> [--limit N] показать сообщения
|
||||
* send <id> <text...> отправить ход, стримит response-события в stdout
|
||||
* interrupt <id> прервать текущий ход
|
||||
* info показать конфиг
|
||||
*
|
||||
* `--server` и `--id` задаются ПОСЛЕ имени subcommand'а (т.е.
|
||||
* `agentik-cli conv-ls --server http://...`), не до — kotlinx.cli не
|
||||
* `agentik-cli conv ls --server http://...`), не до — kotlinx.cli не
|
||||
* шарит опции родителя в subcommand.
|
||||
*
|
||||
* Вложенные subcommands (`conv ls`, `conv new`, ...) реализованы
|
||||
* через [Subcommand.subcommands]: `conv` сам — subcommand, и его
|
||||
* дочерние команды (`ls`, `new`, `show`, `delete`, `rename`)
|
||||
* регистрируются у него.
|
||||
*/
|
||||
@OptIn(ExperimentalCli::class)
|
||||
fun runAgentikCli(args: Array<String>) {
|
||||
val parser = ArgParser(programName = "agentik-cli")
|
||||
val parser = ArgParser(
|
||||
programName = "agentik-cli",
|
||||
// Все аргументы после имени subcommand должны передаваться
|
||||
// В subcommand-парсер, а не парситься на уровне родителя.
|
||||
// Без этого `conv new --server ...` парсится как `conv [--server ...]`
|
||||
// + аргумент "new" → execute родителя, без вложенной команды.
|
||||
strictSubcommandOptionsOrder = true,
|
||||
)
|
||||
|
||||
val subcommands: List<Subcommand> = listOf(
|
||||
ConvLsSubcommand(),
|
||||
ConvNewSubcommand(),
|
||||
ConvShowSubcommand(),
|
||||
ConvDeleteSubcommand(),
|
||||
ConvRenameSubcommand(),
|
||||
val conv = ConvCommand()
|
||||
parser.subcommands(
|
||||
conv,
|
||||
MsgsSubcommand(),
|
||||
SendSubcommand(),
|
||||
InterruptSubcommand(),
|
||||
InfoSubcommand(),
|
||||
)
|
||||
subcommands.forEach { parser.subcommands(it) }
|
||||
|
||||
parser.parse(args)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package pw.binom.agentik.cli.commands
|
||||
|
||||
import kotlinx.cli.ExperimentalCli
|
||||
import kotlinx.cli.Subcommand
|
||||
import pw.binom.agentik.cli.AgentikSubcommand
|
||||
|
||||
/**
|
||||
* Родительская группа `conv`: операции над диалогами.
|
||||
*
|
||||
* Сама команда `agentik-cli conv` (без подкоманды) — no-op:
|
||||
* в kotlinx.cli parent.execute() вызывается ПОСЛЕ leaf.execute(),
|
||||
* поэтому любая работа в execute() дублирует вывод дочерней команды.
|
||||
* Для просмотра дочерних команд есть `agentik-cli conv --help`.
|
||||
*
|
||||
* Дочерние команды регистрируются через [subcommands] в конструкторе.
|
||||
*/
|
||||
@OptIn(ExperimentalCli::class)
|
||||
class ConvCommand : Subcommand("conv", "Операции над диалогами") {
|
||||
init {
|
||||
subcommands(
|
||||
ConvLsSubcommand(),
|
||||
ConvNewSubcommand(),
|
||||
ConvShowSubcommand(),
|
||||
ConvDeleteSubcommand(),
|
||||
ConvRenameSubcommand(),
|
||||
)
|
||||
}
|
||||
|
||||
override fun execute() = Unit
|
||||
}
|
||||
|
||||
abstract class ConvSubcommand(name: String, description: String) : AgentikSubcommand(name, description)
|
||||
+1
-1
@@ -4,7 +4,7 @@ import kotlinx.cli.ArgType
|
||||
import pw.binom.agentik.cli.AgentikSubcommand
|
||||
import pw.binom.agentik.client.AgentikAgent
|
||||
|
||||
class ConvDeleteSubcommand : AgentikSubcommand("conv-delete", "Удалить диалог") {
|
||||
class ConvDeleteSubcommand : ConvSubcommand("delete", "Удалить диалог") {
|
||||
val id by argument(ArgType.String, description = "ID диалога")
|
||||
|
||||
override fun execute() = kotlinx.coroutines.runBlocking {
|
||||
|
||||
@@ -6,7 +6,7 @@ import pw.binom.agentik.cli.AgentikSubcommand
|
||||
import pw.binom.agentik.client.AgentikAgent
|
||||
import pw.binom.agentik.proto.Agent
|
||||
|
||||
class ConvLsSubcommand : AgentikSubcommand("conv-ls", "Список диалогов агента") {
|
||||
class ConvLsSubcommand : ConvSubcommand("ls", "Список диалогов агента") {
|
||||
val limit by option(ArgType.Int, fullName = "limit", description = "Максимум диалогов").default(Agent.PAGE_SIZE)
|
||||
|
||||
override fun execute() = kotlinx.coroutines.runBlocking {
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import kotlinx.cli.default
|
||||
import pw.binom.agentik.cli.AgentikSubcommand
|
||||
import pw.binom.agentik.client.AgentikAgent
|
||||
|
||||
class ConvNewSubcommand : AgentikSubcommand("conv-new", "Создать диалог; печатает id") {
|
||||
class ConvNewSubcommand : ConvSubcommand("new", "Создать диалог; печатает id") {
|
||||
val temp by option(ArgType.Boolean, fullName = "temp", description = "Временный диалог").default(false)
|
||||
|
||||
override fun execute() = kotlinx.coroutines.runBlocking {
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import kotlinx.cli.ArgType
|
||||
import pw.binom.agentik.cli.AgentikSubcommand
|
||||
import pw.binom.agentik.client.AgentikAgent
|
||||
|
||||
class ConvRenameSubcommand : AgentikSubcommand("conv-rename", "Переименовать диалог") {
|
||||
class ConvRenameSubcommand : ConvSubcommand("rename", "Переименовать диалог") {
|
||||
val id by argument(ArgType.String, description = "ID диалога")
|
||||
val title by argument(ArgType.String, description = "Новое название")
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import kotlinx.cli.ArgType
|
||||
import pw.binom.agentik.cli.AgentikSubcommand
|
||||
import pw.binom.agentik.client.AgentikAgent
|
||||
|
||||
class ConvShowSubcommand : AgentikSubcommand("conv-show", "Метаданные диалога") {
|
||||
class ConvShowSubcommand : ConvSubcommand("show", "Метаданные диалога") {
|
||||
val id by argument(ArgType.String, description = "ID диалога")
|
||||
|
||||
override fun execute() = kotlinx.coroutines.runBlocking {
|
||||
|
||||
Reference in New Issue
Block a user