Перейти к основному содержимому

Расширение CLI

VibeOS предоставляет защищённые точки расширения на VibeOSCLI, чтобы обёрточные CLI могли добавлять виджеты, привязки клавиш и настройки макета без переопределения метода run() длиной более 1000 строк. Это сохраняет ваше расширение независимым от внутренних изменений.

Точки расширения​

Доступно пять точек расширения:

ХукНазначениеПереопределять, когда...
_get_extra_tui_widgets()Внедрение виджетов в макетНужен постоянный элемент интерфейса (панель, строка состояния, мини-плеер)
_register_extra_tui_keybindings(kb, *, input_area)Добавление горячих клавишНужны хоткеи (переключение панелей, управление воспроизведением, модальные сочетания)
_build_tui_layout_children(**widgets)Полный контроль над порядком виджетовНужно изменить порядок или обернуть существующие виджеты (редко)
process_command()Добавление пользовательских слеш-командНужна обработка /mycommand (существующий хук)
_build_tui_style_dict()Пользовательские стили prompt_toolkitНужны собственные цвета или стили (существующий хук)

Первые три — новые защищённые хуки. Последние два уже существовали.

Быстрый старт: обёрточный CLI​

#!/usr/bin/env python3
"""my_cli.py — Пример обёрточного CLI, расширяющего VibeOS."""

from cli import VibeOSCLI
from prompt_toolkit.layout import FormattedTextControl, Window
from prompt_toolkit.filters import Condition


class MyCLI(VibeOSCLI):

def __init__(self, **kwargs):
super().__init__(**kwargs)
self._panel_visible = False

def _get_extra_tui_widgets(self):
"""Добавляет переключаемую информационную панель над строкой состояния."""
cli_ref = self
return [
Window(
FormattedTextControl(lambda: "📊 Содержимое моей панели"),
height=1,
filter=Condition(lambda: cli_ref._panel_visible),
),
]

def _register_extra_tui_keybindings(self, kb, *, input_area):
"""F2 переключает пользовательскую панель."""
cli_ref = self

@kb.add("f2")
def _toggle_panel(event):
cli_ref._panel_visible = not cli_ref._panel_visible

def process_command(self, cmd: str) -> bool:
"""Добавляет слеш-команду /panel."""
if cmd.strip().lower() == "/panel":
self._panel_visible = not self._panel_visible
state = "видима" if self._panel_visible else "скрыта"
print(f"Панель теперь {state}")
return True
return super().process_command(cmd)


if __name__ == "__main__":
cli = MyCLI()
cli.run()

Запуск:

cd ~/.vibeos/vibeos-agent
source .venv/bin/activate
python my_cli.py

Справочник хуков​

_get_extra_tui_widgets()​

Возвращает список виджетов prompt_toolkit для вставки в макет TUI. Виджеты появляются между spacer и строкой состояния — над областью ввода, но под основным выводом.

def _get_extra_tui_widgets(self) -> list:
return [] # по умолчанию: без дополнительных виджетов

Каждый виджет должен быть контейнером prompt_toolkit (например, Window, ConditionalContainer, HSplit). Используйте ConditionalContainer или filter=Condition(...), чтобы сделать виджеты переключаемыми.

from prompt_toolkit.layout import ConditionalContainer, Window, FormattedTextControl
from prompt_toolkit.filters import Condition

def _get_extra_tui_widgets(self):
return [
ConditionalContainer(
Window(FormattedTextControl("Статус: подключено"), height=1),
filter=Condition(lambda: self._show_status),
),
]

_register_extra_tui_keybindings(kb, *, input_area)​

Вызывается после регистрации собственных привязок клавиш VibeOS и до построения макета. Добавляйте свои привязки клавиш в kb.

def _register_extra_tui_keybindings(self, kb, *, input_area):
pass # по умолчанию: без дополнительных привязок клавиш

Параметры:

  • kb — Экземпляр KeyBindings для приложения prompt_toolkit
  • input_area — Основной виджет TextArea, если нужно читать или изменять пользовательский ввод
def _register_extra_tui_keybindings(self, kb, *, input_area):
cli_ref = self

@kb.add("f3")
def _clear_input(event):
input_area.text = ""

@kb.add("f4")
def _insert_template(event):
input_area.text = "/search "

Избегайте конфликтов со встроенными привязками клавиш: Enter (отправка), Escape Enter (новая строка), Ctrl-C (прерывание), Ctrl-D (выход), Tab (принять автодополнение). Функциональные клавиши F2+ и комбинации с Ctrl обычно безопасны.

_build_tui_layout_children(**widgets)​

Переопределяйте только когда нужен полный контроль над порядком виджетов. Большинству расширений следует использовать _get_extra_tui_widgets().

def _build_tui_layout_children(self, *, sudo_widget, secret_widget,
approval_widget, clarify_widget, model_picker_widget=None,
spinner_widget=None, spacer, status_bar, input_rule_top,
image_bar, input_area, input_rule_bot, voice_status_bar,
completions_menu) -> list:

Реализация по умолчанию возвращает (виджеты со значением None отфильтровываются):

[
Window(height=0), # якорь
sudo_widget, # запрос пароля sudo (условный)
secret_widget, # запрос секретного ввода (условный)
approval_widget, # подтверждение опасной команды (условный)
clarify_widget, # интерфейс уточняющего вопроса (условный)
model_picker_widget, # оверлей выбора модели (условный)
spinner_widget, # индикатор размышления (условный)
spacer, # заполняет оставшееся вертикальное пространство
*self._get_extra_tui_widgets(), # ВАШИ ВИДЖЕТЫ ЗДЕСЬ
status_bar, # строка состояния модели/токенов/контекста
input_rule_top, # ─── граница над вводом
image_bar, # индикатор прикреплённых изображений
input_area, # ввод текста пользователем
input_rule_bot, # ─── граница под вводом
voice_status_bar, # статус голосового режима (условный)
completions_menu, # выпадающее меню автодополнения
]

Схема макета​

Макет по умолчанию сверху вниз:

  1. Область вывода — прокручиваемая история диалога
  2. Spacer
  3. Дополнительные виджеты — из _get_extra_tui_widgets()
  4. Строка состояния — модель, контекст %, прошедшее время
  5. Панель изображений — количество прикреплённых изображений
  6. Область ввода — подсказка пользователя
  7. Голосовой статус — индикатор записи
  8. Меню автодополнения — предложения автодополнения

Советы​

  • Обновляйте отображение после изменений состояния: вызывайте self._invalidate(), чтобы инициировать перерисовку prompt_toolkit.
  • Доступ к состоянию агента: self.agent, self.model, self.conversation_history — все доступны.
  • Пользовательские стили: Переопределите _build_tui_style_dict() и добавьте записи для своих классов стилей.
  • Слеш-команды: Переопределите process_command(), обработайте свои команды и вызовите super().process_command(cmd) для всего остального.
  • Не переопределяйте run() без крайней необходимости — точки расширения существуют именно для того, чтобы избежать такой связанности.