跳到主要内容

扩展 CLI

Hermes 在 HermesCLI 上暴露了受保护的扩展钩子,使得封装 CLI 可以在不覆盖 1000 多行的 run() 方法的情况下,添加小部件、快捷键和布局自定义功能。这使得你的扩展与内部实现变化保持解耦。

扩展点

共有五个可用的扩展钩子:

钩子用途何时重写
_get_extra_tui_widgets()将小部件注入布局你需要一个持久的 UI 元素(面板、状态栏、迷你播放器)
_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 — Example wrapper CLI that extends 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):
"""Add a toggleable info panel above the status bar."""
cli_ref = self
return [
Window(
FormattedTextControl(lambda: "📊 My custom panel content"),
height=1,
filter=Condition(lambda: cli_ref._panel_visible),
),
]

def _register_extra_tui_keybindings(self, kb, *, input_area):
"""F2 toggles the custom panel."""
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:
"""Add a /panel slash command."""
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()

运行它:

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

钩子参考

_get_extra_tui_widgets()

返回一个要插入 TUI 布局的小部件列表。这些小部件将出现在 分隔符和状态栏之间 —— 位于输入区域之上,主输出区域之下。

def _get_extra_tui_widgets(self) -> list:
return [] # default: no extra widgets

每个小部件都应是一个 prompt_toolkit 容器(例如 WindowConditionalContainerHSplit)。使用 ConditionalContainerfilter=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("Status: connected"), height=1),
filter=Condition(lambda: self._show_status),
),
]

_register_extra_tui_keybindings(kb, *, input_area)

在 Hermes 注册其自身快捷键之后、布局构建之前被调用。将你的快捷键添加到 kb 中。

def _register_extra_tui_keybindings(self, kb, *, input_area):
pass # default: no extra keybindings

参数:

  • kb — prompt_toolkit 应用程序的 KeyBindings 实例
  • 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, spinner_widget, spacer,
status_bar, input_rule_top, image_bar, input_area,
input_rule_bot, voice_status_bar, completions_menu) -> list:

默认实现返回:

[
Window(height=0), # anchor
sudo_widget, # sudo password prompt (conditional)
secret_widget, # secret input prompt (conditional)
approval_widget, # dangerous command approval (conditional)
clarify_widget, # clarify question UI (conditional)
spinner_widget, # thinking spinner (conditional)
spacer, # fills remaining vertical space
*self._get_extra_tui_widgets(), # YOUR WIDGETS GO HERE
status_bar, # model/token/context status line
input_rule_top, # ─── border above input
image_bar, # attached images indicator
input_area, # user text input
input_rule_bot, # ─── border below input
voice_status_bar, # voice mode status (conditional)
completions_menu, # autocomplete dropdown
]

布局图示

从上到下的默认布局:

  1. 输出区域 —— 可滚动的对话历史
  2. 分隔符
  3. 额外小部件 —— 来自 _get_extra_tui_widgets()
  4. 状态栏 —— 模型、上下文百分比、已用时间
  5. 图像栏 —— 附加图像数量
  6. 输入区域 —— 用户提示
  7. 语音状态 —— 录音指示器
  8. 补全菜单 —— 自动补全建议