Свои инструменты
Инструмент — обычная программа: аргументы, stdin, stdout, код возврата. Ядро не знает, на чём она написана, и не требует пересборки себя.
Где лежит и как находится
tools_dir/<имя>/<имя> — бинарь или симлинк; рядом могут лежать schema.json (интерфейс для модели) и selftest.sh.
Ядро обходит каталоги в tools_dir, для каждого зовёт <бинарь> --help (таймаут 5 с, потолок вывода 4000 байт) и берёт оттуда описание для модели. Нет исполняемого файла, упал или пуст --help — тул пропущен, в stderr одна строка. Соседний каталог симлинков ставится в PATH дочерних процессов: run 'read --help' попадает в свой тул, а не в системную утилиту; тул с именем системной команды туда не выкладывается.
Контракт --help
Первая непустая строка — суть в одну строку, вторая — usage:
grep — поиск по файлам: путь, строка, текст совпадения.
grep <шаблон> [<путь>...] [--glob G] [--context N] [--limit N] [--word]
Дальше — что угодно: это читают руками, в описание для модели не едет. Слово stdin в сути или usage означает для ядра, что stdin обязателен.
Два вида интерфейса
Generic — модель присылает args (массив или строка argv) и stdin: {"args": ["--limit", "20", "pattern"], "stdin": ""}. Строка режется по пробелам, пробел внутри аргумента так не передать.
Типизированный — рядом с бинарём лежит schema.json: JSON Schema объекта плюс ключ x-snens, описывающий, как поля становятся argv и stdin. Модель видит настоящие поля, argv собирает ядро через serde_json. Нет type: object или properties — тул остаётся generic, причина уходит в stderr; битый schema.json тул не теряет.
Что ядро даёт любому вызову
cwd — рабочий каталог, разрешается от каталога сессии (его меняет cd внутри run); timeout_seconds — таймаут процесса, по умолчанию из конфига. Ошибка тула не прерывает ход: она возвращается результатом.
SNENS_SESSION, SNENS_CONFIG, SNENS_SESSIONS_DIR, SNENS_CWD — сессия, конфиг, каталог сессий, точка отсчёта относительных путей. SNENS_MAIL_DIR, SNENS_PARENT — ящик подагентов и id родителя (только у подагента). SNENS_CONTEXT_PATH, SNENS_ATTACH_DIR — зеркало контекста и каталог вложений вызова. SNENS_PRIV, SNENS_APPROVAL_SOCK — брокер привилегий и сокет одобрений. Нет переменной — тул не знает контекста.
Пример: тул wc-lines
tools/wc-lines/wc-lines:
#!/bin/sh
# wc-lines — сколько строк в файле.
# wc-lines <путь> [--no-empty]
path=""; noempty=0
while [ $# -gt 0 ]; do case "$1" in --no-empty) noempty=1 ;; *) path="$1" ;; esac; shift; done
[ -n "$path" ] || { echo "wc-lines: нужен путь" >&2; exit 2; }
if [ "$noempty" = 1 ]; then n=$(grep -c '[^[:space:]]' "$path"); else n=$(wc -l < "$path"); fi
echo "$path: $n"
tools/wc-lines/schema.json:
{
"type": "object",
"properties": {
"path": {"type": "string", "description": "Файл: абсолютный или относительно рабочего каталога"},
"no_empty": {"type": "boolean", "description": "Не считать пустые строки"}
},
"required": ["path"],
"x-snens": {
"argv": [{"field": "path"}, {"flag": "--no-empty", "when_field": "no_empty"}],
"stdin": {"mode": "none"}
}
}
элемент x-snens.argv | результат |
|---|---|
{"field": "path"} | значение поля отдельным аргументом |
{"flag": "--offset", "field": "offset"} | --offset N, если поле передано |
{"flag": "--append", "when_field": "append"} | --append, если поле истинно |
x-snens.stdin: none — ничего; field с именем поля — сырое значение поля (так write отдаёт содержимое); json со списком fields — объект из этих полей, сериализованный ядром (так edit передаёт правку без второго уровня экранирования). Из parameters ключ вырезается: провайдер модели о нём не знает. Проверка: tools/wc-lines/wc-lines --help — суть и usage; snens tools --json — определение, которое видит модель.