skills: парсер YAML-скилов (opencode-формат)

Новый KMP-модуль :skills (9 целей, зеркалит :proto). Парсит скилы
в формате opencode: YAML-фронтматтер с name+description, затем markdown
body.

  - SkillFile(name, description, body) — @Serializable результат.
  - SkillParseError — sealed-иерархия ошибок: Empty, MissingOpeningFence,
    MissingClosingFence, InvalidYaml, SchemaMismatch, BlankName.
  - SkillParser.parse(raw): SkillParseResult + parseOrThrow(raw): SkillFile.
    Обрабатывает BOM, CRLF, имена с двоеточиями (backend:spring:db-base),
    multiline-YAML, пустое тело; неизвестные ключи фронтматтера игнорирует.
  - SkillParserTest — 16 тестов, 0 failures.

YAML-библиотека — com.charleskorn.kaml 0.104.0 (не JetBrains,
интеграция с kotlinx @Serializable). Грабли kaml 0.104: параметр
ignoreUnknownKeys переименован в strictMode (инвертирован);
missing-required-field прилетает как YamlException, не
SerializationException — различаем по сообщению.

Проверка: :skills:jvmTest 16/16, :skills:assemble (9 целей) — зелёные.
This commit is contained in:
2026-09-13 21:28:47 +03:00
parent cad4d5fc7c
commit f4caab940e
7 changed files with 387 additions and 0 deletions
+4
View File
@@ -4,6 +4,7 @@ kotlinx-serialization = "1.11.0"
kotlinx-coroutines = "1.11.0"
ktor = "3.1.3"
a2a = "1.0.0-SNAPSHOT"
kaml = "0.104.0"
litert = "7"
sqldelight = "2.3.2"
@@ -19,6 +20,9 @@ a2a-shared = { module = "pw.binom.a2a:shared", version.ref = "a2a" }
a2a-client = { module = "pw.binom.a2a:client", version.ref = "a2a" }
a2a-server = { module = "pw.binom.a2a:server", version.ref = "a2a" }
# --- YAML для парсинга скилов (opencode-style frontmatter). KMP. ---
kaml = { module = "com.charleskorn.kaml:kaml", version.ref = "kaml" }
# --- litert-kmp (pw.binom.litert) — universal LLM wrapper ---
litert-api = { module = "pw.binom.litert:litert-api", version.ref = "litert" }
litert-openai = { module = "pw.binom.litert:litert-openai-jvm", version.ref = "litert" }