Pular para o conteúdo principal

Documentação oficial desta página · v0.20.6

Estender a CLI

O Hermes expõe ganchos de extensão protegidos no HermesCLI, para que CLIs derivadas acrescentem widgets, atalhos de teclado e ajustes de layout sem sobrescrever o método run(), que tem mais de mil linhas. Isso mantém a sua extensão desacoplada das mudanças internas.

Pontos de extensão​

São cinco costuras disponíveis:

GanchoPara que serveSobrescreva quando…
_get_extra_tui_widgets()Injetar widgets no layoutVocê precisa de um elemento fixo (painel, linha de status, mini-player)
_register_extra_tui_keybindings(kb, *, input_area)Acrescentar atalhosVocê precisa de teclas de atalho (mostrar painéis, controles, atalhos modais)
_build_tui_layout_children(**widgets)Controle total da ordem dos widgetsVocê precisa reordenar ou embrulhar widgets existentes (raro)
process_command()Acrescentar comandos de barraVocê precisa tratar /meucomando (gancho que já existia)
_build_tui_style_dict()Estilos próprios do prompt_toolkitVocê precisa de cores ou estilo próprios (gancho que já existia)

Os três primeiros são ganchos protegidos novos. Os dois últimos já existiam.

Começando: uma CLI derivada​

#!/usr/bin/env python3
"""my_cli.py — exemplo de CLI derivada que estende o Hermes."""

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


class MyCLI(HermesCLI):

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

def _get_extra_tui_widgets(self):
"""Acrescenta um painel de informação, que aparece e some, acima da barra de status."""
cli_ref = self
return [
Window(
FormattedTextControl(lambda: "📊 Conteúdo do meu painel"),
height=1,
filter=Condition(lambda: cli_ref._panel_visible),
),
]

def _register_extra_tui_keybindings(self, kb, *, input_area):
"""F2 mostra e esconde o painel."""
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:
"""Acrescenta um comando /panel."""
if cmd.strip().lower() == "/panel":
self._panel_visible = not self._panel_visible
state = "visible" if self._panel_visible else "hidden"
print(f"Panel is now {state}")
return True
return super().process_command(cmd)


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

Rodando:

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

Referência dos ganchos​

_get_extra_tui_widgets()​

Devolve uma lista de widgets do prompt_toolkit para inserir no layout. Os widgets aparecem entre o espaçador e a barra de status — acima da área de entrada e abaixo da saída principal.

def _get_extra_tui_widgets(self) -> list:
return [] # padrão: nenhum widget extra

Cada widget deve ser um container do prompt_toolkit (Window, ConditionalContainer, HSplit). Use ConditionalContainer ou filter=Condition(...) para poder mostrar e esconder.

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

def _get_extra_tui_widgets(self):
return [
ConditionalContainer(
Window(FormattedTextControl("Status: connected"), height=1),
filter=Condition(lambda: self._show_status),
),
]

_register_extra_tui_keybindings(kb, *, input_area)​

Chamado depois de o Hermes registrar os próprios atalhos e antes de montar o layout. Acrescente os seus ao kb.

def _register_extra_tui_keybindings(self, kb, *, input_area):
pass # padrão: nenhum atalho extra

Parâmetros:

  • kb — a instância de KeyBindings da aplicação prompt_toolkit
  • input_area — o widget TextArea principal, se você precisar ler ou manipular a entrada
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 "

Evite conflito com os atalhos embutidos: Enter (enviar), Escape Enter (nova linha), Ctrl-C (interromper), Ctrl-D (sair), Tab (aceitar sugestão). Teclas de função a partir de F2 e combinações com Ctrl costumam ser seguras.

_build_tui_layout_children(**widgets)​

Sobrescreva só quando precisar de controle total da ordem. A maioria das extensões deve usar o _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:

A implementação padrão devolve (widgets None são filtrados):

[
Window(height=0), # âncora
sudo_widget, # pedido de senha do sudo (condicional)
secret_widget, # entrada de segredo (condicional)
approval_widget, # aprovação de comando perigoso (condicional)
clarify_widget, # interface de pergunta de esclarecimento (condicional)
model_picker_widget, # sobreposição do seletor de modelo (condicional)
spinner_widget, # indicador de pensamento (condicional)
spacer, # preenche o espaço vertical restante
*self._get_extra_tui_widgets(), # SEUS WIDGETS ENTRAM AQUI
status_bar, # linha de status com modelo, tokens e contexto
input_rule_top, # ─── borda acima da entrada
image_bar, # indicador de imagens anexadas
input_area, # entrada de texto
input_rule_bot, # ─── borda abaixo da entrada
voice_status_bar, # status do modo de voz (condicional)
completions_menu, # lista de autocompletar
]

Diagrama do layout​

De cima para baixo:

  1. Área de saída — histórico da conversa, com rolagem
  2. Espaçador
  3. Widgets extras — de _get_extra_tui_widgets()
  4. Barra de status — modelo, porcentagem de contexto, tempo decorrido
  5. Barra de imagens — quantidade de imagens anexadas
  6. Área de entrada — o texto do usuário
  7. Status de voz — indicador de gravação
  8. Menu de autocompletar — sugestões

Dicas​

  • Invalide a tela depois de mudar estado: chame self._invalidate() para forçar um redesenho.
  • Acesse o estado do agente: self.agent, self.model e self.conversation_history estão disponíveis.
  • Estilos próprios: sobrescreva o _build_tui_style_dict() e acrescente entradas para as suas classes de estilo.
  • Comandos de barra: sobrescreva o process_command(), trate os seus comandos e chame super().process_command(cmd) para todo o resto.
  • Não sobrescreva o run() a menos que seja indispensável — os ganchos existem justamente para evitar esse acoplamento.