擴展 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 [] # 默認:沒有額外的小部件
每個小部件都應是一個 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("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 # 默認值:沒有額外的鍵綁定
參數:
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), # 錨
sudo_widget, # sudo 密碼 prompt (有條件)
secret_widget, # 秘密輸入prompt(有條件)
approval_widget, # 危險指揮批准(有條件)
clarify_widget, # 澄清問題 UI(有條件)
spinner_widget, # 思維旋轉器(有條件)
spacer, # 填充剩餘的垂直空間
*self._get_extra_tui_widgets(), # YOUR WIDGETS 去 HERE
status_bar, # 型號/token/context 狀態線
input_rule_top, # ── 輸入上方的邊框
image_bar, # 附加圖像指示器
input_area, # 用戶文本輸入
input_rule_bot, # ── 輸入框下方的邊框
voice_status_bar, # 語音模式狀態(有條件)
completions_menu, # 自動完成下拉菜單
]
佈局圖示
從上到下的默認佈局:
- 輸出區域 —— 可滾動的對話歷史
- 分隔符
- 額外小部件 —— 來自
_get_extra_tui_widgets() - 狀態欄 —— 模型、上下文百分比、已用時間
- 圖像欄 —— 附加圖像數量
- 輸入區域 —— 用戶提示
- 語音狀態 —— 錄音指示器
- 補全菜單 —— 自動補全建議