Carriage — не «агент» и не «волшебная кнопка». Это управляемая, надёжная и простая «вязальная машинка для кода»: вы задаёте режим, контекст, а инструмент аккуратно «вяжет» изменения, фиксирует отпечаток каждого прохода, показывает отчёты и всегда даёт возможность остановки.
Принципы:
- Прямой путь к результату: минимальные шаги, быстрый полезный отклик, прозрачные отчёты.
- Контроль и воспроизводимость: изменения оформляются блоками, dry-run → apply выполняются явно; patch всегда через git-движок.
- Прозрачность: видны логи трафика, стоимость (doc-cost), состояние документа и отпечатки (FINGERPRINT).
- Литературное программирование и Reproducible Research: один Org-документ хранит задачу, контекст, код и полученные изменения; история в блоках и отпечатках делает процесс повторяемым.
Требования: Emacs 27+, установлен Git, локальный проект (без TRAMP для файловых операций и git).
Минимальная установка Carriage и gptel (OpenAI-совместимый API; ключ в окружении OPENAI_API_KEY).
;; gptel (минимум): задайте OPENAI_API_KEY в окружении
(use-package gptel
:ensure t
:init
(setq gptel-model 'gpt-4o-mini) ;; замените на доступную модель
(setq gptel-backend
(gptel-make-openai "OpenAI"
:key (getenv "OPENAI_API_KEY")
:host "api.openai.com"
:endpoint "/v1/chat/completions")))
;; Carriage из локального клона:
;; git clone https://github.com/<you>/carriage ~/.emacs.d/site-lisp/carriage
(use-package carriage
:load-path "~/.emacs.d/site-lisp/carriage/lisp"
:commands (carriage-mode carriage-global-mode)
:init
;; (carriage-global-mode 1) ;; опционально: глобальный префикс/меню под C-c e
)Альтернатива (из исходников проекта): добавьте в init.el:
(add-to-list 'load-path (expand-file-name "lisp" default-directory))
(require 'carriage)Переключение языка UI (ru/en):
;; ru или en
(setq carriage-i18n-locale 'ru)Цель: быстро «задать вопрос» и увидеть потоковый ответ.
- Откройте Org-файл и включите режим: M-x carriage-mode
- Переключите Intent на Ask: C-c e i
- Отправьте запрос: C-c RET
- Остановите поток при необходимости: C-c e k
- Где смотреть:
- Состояние/индикаторы — в modeline (см. «UI и modeline»)
- Логи трафика — C-c e T (или M-x switch-to-buffer → carriage-traffic:…)
- Отчёт — C-c e r
Подсказки:
- Intent: Ask — диалог/вопросы; Code — запросы на генерацию кода/патчей; Hybrid — смешанный режим. Переключение: C-c e i.
- Стоимость документа (doc-cost) накапливается по факту завершения запросов и показывается отдельным бейджем (см. ниже).
Цель: получить begin_patch, безопасно применить и увидеть отчёт.
- Переключите Intent: C-c e i → выберите Code или Hybrid
- Сформулируйте задачу. Просите модель:
- Генерировать один или несколько файлов или вснести правки (если задан контекст, сама Carriage не найдёт содержимое файла, если только его наличие через карту)
- C-c RET - отправьте запрос
- Получили patch-блок(и) → примените:
- Apply под точкой/в регионе: C-c C-c
- Открыть отчёт: C-c e r (см. ok/skip/fail)
- После полного успеха:
- В Messages появится сводка (carriage-announce)
- Заголовок применённого
#+begin_patchбудет аннотирован (:applied t …); содержимое может быть скрыто политикой (см. «Настройки»)
Важно:
- Патчи всегда идут через git-движок (даже если выбран другой движок в UI).
- Файловые операции (create/rename/delete/sre/aibo) могут выполняться локальным emacs-движком; при включённом stage-policy=’index для git — через git add/rm/mv.
Цель: управлять перечнем файлов, которые модель читает на вход.
Что такое begin_context: это блок со списком путей; их текущее содержимое добавляется к подсказке (в пределах лимитов профиля/скоупа).
Два способа получить begin_context:
- Попросить модель в режиме Ask: «Собери begin_context по карте проекта для этой задачи».
- Опционально — вставлять через «Context Navigator»: C-c n i
Профиль и скоуп:
- Переключить профиль P1/P3: C-c e t P (влияет на лимиты контекста)
- Переключить scope all/last: C-c e t s
- Состав контекста и счётчик [Ctx:N] видны в modeline
- Описать задачу (Ask/Code/Hybrid) → отправить (C-c e RET)
- Получить begin_patch → Dry-run (C-c e d) → Apply (C-c e a) → Отчёт (C-c e r)
- При успехе убедиться в :applied t и сводке в Messages
- Повторить до «зелёного» отчёта
Когда освоен простой цикл — добавьте структуру и контекст:
- Минимум:
#+begin_task/#+begin_analysis/#+begin_plan/#+begin_patch - Контекст:
#+begin_contextс путями (может генерироваться моделью или навигатором) - По ходу итераций просите модель уточнять/расширять begin_context, менять Intent (Ask ↔ Code/Hybrid) и дополнять план
Опционально: удобные Org-аббревиатуры для вставки блоков
(require 'carriage-typedblocks-templates)
(carriage-typedblocks-install-structure-templates)Ниже — ориентир по ключевым сегментам (внешний вид и набор могут отличаться в зависимости от профиля/настроек).
| Сегмент | Что показывает | Клик/действие (пример) | Tooltip/подсказка (пример) | Быстрая клавиша |
|---|---|---|---|---|
| Intent | Ask / Code / Hybrid | Меню Intent | Текущий Intent и эффект на подсказку | C-c e i |
| Model | Текущая модель gptel | Меню выбора модели | Имя и детали модели | C-c e m |
| Engine | Движок применения (policy) | Меню выбора движка | Для git — policy: in-place/wip/ephemeral | C-c e E |
| State | idle/sending/streaming/error | — | Стадия транспорта, спиннер/ожидание | — |
| Apply status | Сводка последнего отчёта | Открыть отчёт | ok/skip/fail/total | C-c e r |
| [Ctx:N] | Кол-во файлов в контексте | Включатели/лимиты контекста | Источники (doc/gptel/visible/patch/map) | C-c e t … (см. «Клавиши») |
| Doc-cost | Суммарная стоимость документа | — | Откуда берётся и время обновления | — |
| Abort | Прерывание текущего потока | — | Завершит процесс, очистит ресурсы | C-c e k |
Doc-cost (коротко):
- Источник: строки
#+CARRIAGE_RESULT/ служебный FINGERPRINT в документе после завершения запросов. - Обновление: автоматически по завершении; отображается сумма известных стоимостей.
- Валюта: символ задаётся
carriage-pricing-currency-symbol.
Префиксные (C-c e …):
| Клавиша | Действие |
|---|---|
| SPC | Меню Carriage |
| ? | Справка по клавишам |
| RET | Отправить буфер |
| M-RET | Отправить поддерево |
| d | Dry-run под точкой |
| a | Apply под точкой/в регионе |
| A | Применить последнюю итерацию |
| r | Открыть отчёт |
| k | Abort (прервать поток) |
| m | Выбрать модель |
| S | Выбрать Suite |
| i | Переключить Intent |
| E | Выбрать движок применения |
| t g | Контекст: gptel |
| t f | Контекст: файлы из begin_context |
| t p | Контекст: файлы из begin_patch |
| t m | Контекст: карта проекта (begin_map) |
| t v | Контекст: видимые буферы |
| t a | Scope = all |
| t l | Scope = last |
| t s | Переключить scope |
| t P | Профиль контекста P1/P3 |
| w | Переключиться на WIP-ветку (git) |
| R | Soft reset |
| cc | Commit: все изменения |
| cl | Commit: последняя итерация |
| L | Общий лог |
| T | Лог трафика |
| e | Открыть буфер Carriage |
| f | Чат по текущему файлу |
| n | Создать документ задачи (если доступно) |
Внутри carriage-mode (контекстные):
| Клавиша | Действие |
|---|---|
| C-c C-c | Контекстно: применить patch / собрать план |
| C-c ! | Применить последнюю итерацию |
| C-c RET | Отправить буфер |
Пользовательские/опциональные (пример привязки):
| Клавиша | Действие |
|---|---|
| C-c n e | Запуск «ветвления» (branching transient), пример |
| C-c n i | Внешний context-navigator (пример) |
Пример биндинга (пользовательская привязка):
(with-eval-after-load 'carriage-mode
(define-key carriage-mode-map (kbd "C-c n e") #'carriage-branching-transient)
(define-key carriage-mode-map (kbd "C-c n i") #'my-context-navigator-insert))| Категория | Переменная | Назначение (кратко) |
|---|---|---|
| Контекст | carriage-mode-include-doc-context | Включать begin_context из документа |
| carriage-mode-include-visible-context | Включать видимые буферы (терминалы — хвост N строк) | |
| carriage-mode-include-gptel-context | Включать внешние пути из gptel (если настроено) | |
| carriage-mode-include-patched-files | Включать файлы, упомянутые в begin_patch | |
| carriage-mode-context-max-files | Лимит количества файлов в контексте | |
| carriage-mode-context-max-total-bytes | Лимит общего объёма контента (байты) | |
| Скоуп/Профиль | carriage-doc-context-scope | ‘all или ‘last (какие begin_context учитывать) |
| (переключатели тумблеров) | t a/t l/t s — scope; t P — профиль P1/P3 | |
| Apply/ветки | carriage-git-branch-policy | Политика git: in-place / wip / ephemeral |
| carriage-apply-stage-policy | Стадирование: none / index | |
| carriage-apply-applied-block-policy | Пост-обработка удачных patch (annotate/none) | |
| carriage-apply-strip-body-on-annotate | При annotate — очищать тело блока | |
| Стоимость | carriage-pricing-currency-symbol | Символ валюты (UI/бейджи/подсказки) |
| Локализация | carriage-i18n-locale | ‘ru или ‘en (надписи/подсказки в UI) |
Выбор движка применения (C-c e E) отражает правила:
- Для git-движка показываются только «комбо»: git:in-place / git:wip / git:ephemeral (bare «git» скрыт).
- patch всегда выполняется git-движком, независимо от выбранного «общего» движка.
- Tooltip у Engine показывает текущую политику веток.
Carriage «прикалывает» тонкие следы прямо в Org:
#+CARRIAGE_RESULT— метаданные последнего запроса (backend, модель, usage, стоимость, timestamp).- FINGERPRINT — служебная метка итерации, предотвращает дубликаты, помогает агрегировать стоимость.
- Эти строки и служебные свойства не попадают в подсказку модели; они для воспроизводимости и UI.
- TRAMP-пути не поддерживаются в v1 (поиск корня, файловые операции и git — локальные).
- Пути нормализуются и проверяются границами проекта; «опасные» или внешние пути отклоняются.
- patch всегда через git-движок; emacs-движок обслуживает локальные ops.
- Прерывание (Abort) и сторожевой таймер защищают от зависаний; состояние и процессы очищаются автоматически.
- «patch unsupported by emacs engine»: выберите git-движок (C-c e E) и один из git:in-place/wip/ephemeral; убедитесь, что проект — git-репозиторий.
- Нет Git: инициализируйте репозиторий (git init) или используйте только файловые операции до инициализации.
- Нет OPENAI_API_KEY / ошибки gptel: установите ключ в окружение, проверьте модель в gptel.
- [Ctx:?] не обновляется: подождите 1–2 секунды (асинхронно) или перепроверьте тумблеры контекста (C-c e t …); откройте лог трафика (C-c e T).
- Как прервать зависший поток: C-c e k; затем просмотрите общий лог (C-c e L) и лог трафика (C-c e T).
- Стоимость документа не видна: сделайте хотя бы один успешный запрос; doc-cost строится по
#+CARRIAGE_RESULT.
- Спецификации: ./spec/index.org
- apply-pipeline, engines, context-integration, ui, security, testing, pricing
- Исходные модули: ./lisp/*.el
- Тесты: ./test/*
См. файл LICENSE в корне репозитория.