snens

Свои инструменты

Инструмент — обычная программа: аргументы, 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 — определение, которое видит модель.

Дальше: скиллы · контекст