Господа, сегодня я рад представить свой новый проект!

Для начала должен сказать, что я в основном пишу на Kotlin. В Kotlin у нас есть проблема с просмотром и исследованием исходного кода сторонних библиотек. Я писал и на TypeScript, и на Go, и на Kotlin, и могу сказать, что завидую тем, кто пишет на TypeScript, потому что агенты, когда работают с ним, могут просто залезть в node_modules, ripgrep’нуть эту директорию и найти нужный код сразу же, буквально мгновенно, в скачанных кэшах.

В сравнении с этим Kotlin, особенно мультиплатформа, это пытка. Агенты раньше вообще не могли смотреть исходники, просто галлюцинировали код. Сейчас они поумнели и сами пытаются решить проблемы с тем, что не знают API какой-нибудь библиотеки или найти нужную перегрузку функций, через поиск по файловой системе. Но для них это очень сложно даже со всеми разрешениями, кэшами и с условием того, что все зависимости уже давно скачаны. Поиск одной зависимости может занять до 10-15к токенов контекста, поэтому…

Представляю ksrc!

Это CLI утилита, которая позволяет агентам в одну строчку увидеть исходники любых Kotlin-библиотек

С ksrc ваш агент будет смотреть исходники примерно так:

$ ksrc search "pro.respawn.apiresult:core*" -q "recover"
pro.respawn.apiresult:core:2.1.0!/commonMain/pro/respawn/apiresult/ApiResult.kt:506:42:public inline infix fun <T> ApiResult<T>.recover(
...

$ ksrc cat pro.respawn.apiresult:core:2.1.0!/commonMain/pro/respawn/apiresult/ApiResult.kt --lines 480,515
...
@JvmName("recoverTyped")
public inline infix fun <reified T : Exception, R> ApiResult<R>.recover(
    another: (e: T) -> ApiResult<R>
)
...

2 команды - исходник найден, с фильтрацией по версии, зависимостью, и автоматическим скачиванием и распаковкой.

Как это выглядело без ksrc?

Без ksrc поиск выглядел на практике у моих агентов так:

$ rg --files -g "ApiResult.kt" /Users/nek/.gradle/caches

$ rg "ApiResult\\.recover|recover\\(" /Users/nek/Developer/Respawn/Backend

$ rg --files -g "*apiresult*" /Users/nek/.gradle/caches

$ ls /Users/nek/.gradle/caches
9.2.1		CACHEDIR.TAG	journal-1
build-cache-1	jars-9		modules-2

$ rg --files -g "*apiresult*" /Users/nek/.gradle/caches/modules-2/files-2.1

$ rg --files -g "*apiresult*" /Users/nek/.gradle/caches/jars-9

$ fd -i apiresult /Users/nek/.gradle/caches/modules-2
/Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult/
/Users/nek/.gradle/caches/modules-2/metadata-2.107/descriptors/pro.respawn.apiresult/
$ ls /Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult
core			core-iosarm64		core-jvm
core-android		core-iossimulatorarm64	core-wasm-js
$ ls /Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult/core-jvm
2.1.0
$ ls /Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult/core-jvm/2.1.0
193901bf1e2ecee192d92363d99b2e056467be28
938d7fb2b3cbd2806baac501f75182b9734ee5e1
ac2afbf602985d4257dcae7a6b90713585291627
b8101c9a149083295b708f4010e7c501840c5d8d
$ ls /Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult/core-jvm/2.1.0/193901bf1e2ecee192d92363d99b2e056467be28
core-jvm-2.1.0-sources.jar
$ jar tf /Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult/core-jvm/2.1.0/193901bf1e2ecee192d92363d99b2e056467be28/core-jvm-2.1.0-sources.jar | rg "ApiResult"
commonMain/pro/respawn/apiresult/ApiResult.kt
$ unzip -p /Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult/core-jvm/2.1.0/193901bf1e2ecee192d92363d99b2e056467be28/core-jvm-2.1.0-sources.jar commonMain/pro/respawn/apiresult/ApiResult.kt | rg -n "recover"
...
$ unzip -p /Users/nek/.gradle/caches/modules-2/files-2.1/pro.respawn.apiresult/core-jvm/2.1.0/193901bf1e2ecee192d92363d99b2e056467be28/core-jvm-2.1.0-sources.jar commonMain/pro/respawn/apiresult/ApiResult.kt | nl -ba | sed -n '490,510p'
...
public inline infix fun <reified T : Exception, R> ApiResult<R>.recover(
   another: (e: T) -> ApiResult<R>
)
...

15 (!) ходов, куча токенов мышления, куча мусора в контексте и рандомные разархивированные мусорные файлы в вашей системе - просто чтобы увидеть один метод!

Всё из-за “гениальной” системы организации кэшей у Gradle: агентам нужно копаться в каких-то захешированных папках, которые создает Gradle, тысячах директорий в каких-то modules-2.1 и так далее. Процесс выглядит так:

А если исходников нет, тогда вообще нужно использовать что-нибудь типа javap для декомпиляции исходников, просто для того, чтобы посмотреть, как выглядит одна функция в какой-нибудь библиотеке от Google.

Моя утилита запаковывает все описанные выше шаги в две команды: ksrc search и ksrc cat - и выводит красиво отформатированный результат, который агент может комбинировать с другими командами и дорабатывать скриптами.

Интеграция с AI агентами

Я также наготовил для вас Claude плагин со скиллом для использования ksrc для ваших агентов, чтобы они сразу же могли им пользоваться когда нужно, сами по себе, без вашего участия или промптинга, и также скилл для Codex.

Codex написал эту утилиту сам для себя и полностью самостоятельно на Go - языке, в котором я вообще ничего не понимаю, никогда в жизни не написал и не прочитал ни одной строчки Go. И сам запаковал его в один файл, который вам достаточно скачать с помощью скрипта на GitHub, и настроил для вас интеграцию с агентами.

В ближайшее время займусь публикацией через Homebrew и какой-нибудь вариант для Linux. Буду рад вашему фидбеку в соцсетях. Тем, кто разрабатывает на Kotlin, надеюсь, это будет так же полезно, как мне.