80 lines
5.9 KiB
Markdown
80 lines
5.9 KiB
Markdown
Проект: /root/WORK/memo (Kotlin/JVM). Заказ по CLI — руками проверяемые команды.
|
||
|
||
Правки:
|
||
- memo-cli/build.gradle.kts: добавить application-плагин и зависимость на :memo-core
|
||
- новый файл memo-cli/src/main/kotlin/memo/cli/Main.kt
|
||
- новый файл memo-cli/src/test/kotlin/memo/cli/CliArgsTest.kt
|
||
|
||
## Настройка модуля memo-cli
|
||
|
||
build.gradle.kts модуля:
|
||
plugins { kotlin("jvm"); application }
|
||
application { mainClass.set("memo.cli.MainKt") }
|
||
dependencies { implementation(project(":memo-core")) }
|
||
|
||
## Main.kt — контракт команд
|
||
|
||
fun main(args: Array<String>)
|
||
|
||
Разбор аргументов вынести в чистую функцию (тестируемую без процесса):
|
||
sealed interface Cmd
|
||
data class IndexCmd(val path: String) : Cmd
|
||
data class SearchCmd(val path: String, val query: String, val k: Int, val mode: SearchMode, val json: Boolean) : Cmd
|
||
data class StatusCmd(val path: String) : Cmd
|
||
data class McpProbeCmd(val tool: String, val argsJson: String) : Cmd
|
||
object HelpCmd : Cmd
|
||
fun parseArgs(args: Array<String>): Cmd // при ошибке — HelpCmd
|
||
|
||
Правила разбора:
|
||
- `index <path>` → IndexCmd
|
||
- `search <path> <query> [--k N] [--mode hybrid|lex|vec] [--json]` → SearchCmd
|
||
(по умолчанию k = 8, mode = HYBRID); неизвестный флаг или нечисловой --k → HelpCmd
|
||
- `status <path>` → StatusCmd
|
||
- `mcp-probe --tool <name> [--args <json>]` → McpProbeCmd (args по умолчанию "{}")
|
||
- нет аргументов, `--help`, `-h`, неизвестная команда → HelpCmd
|
||
|
||
Поведение команд:
|
||
1. `index <path>`: определить корень так — если path оканчивается на "/.memo" или содержит его, взять родителя;
|
||
каталог `X` считается коллекцией, если в нём есть файлы *.md (искать на глубине до 2 от path).
|
||
Для каждой найденной коллекции: создать каталог `<коллекция>/.memo`, базу `<коллекция>/.memo/index.db`,
|
||
Db(...).init(), затем Indexer(db, embedder).indexTree(коллекция).
|
||
Путь к модели: переменная окружения MEMO_MODEL_DIR, иначе /root/WORK/memo/models/siglip2.
|
||
Печатать по строке на коллекцию: `индексировано: <N> обновлено, <файлов всего>` и в конце
|
||
`итого: <сумма> обновлено в <число коллекций> коллекциях`.
|
||
2. `search <path> <query>`: path — ЛИБО коллекция, ЛИБО файл *.md (тогда коллекция = его каталог),
|
||
ЛИБО корень, содержащий коллекции (тогда искать по всем коллекциям внутри, объединив результаты
|
||
по убыванию score и обрезав до k).
|
||
Модель/эмбеддер обязателен: без него базы не откроются — это нормально.
|
||
Refresh-хук передавать обязательно: он делает Indexer(db, embedder).indexTree(root) перед поиском.
|
||
Без `--json` печатать человекочитаемо:
|
||
`<score с 3 знаками> <path>:<line> <heading>`
|
||
далее текст чанка с отступом 4 пробела, обрезанный до 300 символов.
|
||
С `--json` печатать ОДИН JSON-массив, без пояснений и без лишних строк:
|
||
[{"path":"...","line":12,"heading":"...","score":0.51,"text":"..."}]
|
||
Если ничего не найдено — пустой JSON-массив `[]` (в режиме --json) и код возврата 0.
|
||
3. `status <path>`: по каждой коллекции печатать `<<коллекция>>: файлов <N>, чанков <M>, индекс <дата ISO>`
|
||
(дата — максимальный indexed_at из files).
|
||
4. `mcp-probe --tool <name> --args <json>`: выполнить то же, что делает MCP-инструмент, и напечатать
|
||
результат в формате MCP-ответа:
|
||
{"content":[{"type":"text","text":"<результат>"}],"isError":false}
|
||
Поддерживаемые инструменты на этом шаге: memo_search (аргументы path, query, k, mode) и memo_status (path).
|
||
memo_reindex — вернуть isError: true с текстом "not implemented yet".
|
||
|
||
## CliArgsTest.kt — ровно 5 тестов, имена ровно такие
|
||
|
||
1. `indexCommandParsed` — ["index", "/tmp/x"] → IndexCmd с path "/tmp/x".
|
||
2. `searchDefaultsAreK8HybridNoJson` — ["search", "/tmp/x", "вопрос"] → SearchCmd(k = 8, mode = HYBRID, json = false).
|
||
3. `searchFlagsParsed` — ["search", "/tmp/x", "вопрос", "--k", "3", "--mode", "lex", "--json"] → k = 3, mode = LEX, json = true.
|
||
4. `badKValueFallsBackToHelp` — ["search", "/tmp/x", "вопрос", "--k", "abc"] → HelpCmd.
|
||
5. `unknownCommandFallsBackToHelp` — ["сломать"] → HelpCmd.
|
||
|
||
После: ./gradlew :memo-cli:test --rerun-tasks и ./gradlew :memo-cli:installDist — обе команды зелёные.
|
||
Проверь руками, что бинарь появился: ls memo-cli/build/install/memo-cli/bin/memo
|
||
Коммит: git add -A && git commit -m "cli: index/search/status/mcp-probe"
|
||
|
||
СТРОГИЕ ЗАПРЕТЫ:
|
||
- Не выводить план текстом; сразу правь файлы.
|
||
- Не трогать memo-core (ни src/main, ни src/test), кроме использования его публичного API.
|
||
- Не добавлять зависимости, кроме :memo-core.
|
||
- Не создавать другие файлы.
|