fix: initialize self.config in HermesCLI to fix AttributeError on slash commands

HermesCLI.__init__ never assigned self.config, causing an
AttributeError ('HermesCLI' object has no attribute 'config')
whenever an unrecognized slash command fell through to the
quick_commands check on line 2838. This affected skill slash
commands like /x-thread-creation since the quick_commands lookup
runs before the skill command check.

Set self.config = CLI_CONFIG in __init__ to match the pattern used
by the gateway (run.py:199).
This commit is contained in:
teknium1
2026-03-11 02:32:43 -07:00
parent f5324f9aa5
commit bd2606a576

113
cli.py
View File

@@ -1244,6 +1244,10 @@ class HermesCLI:
self._command_running = False
self._command_status = ""
# Background task tracking: {task_id: threading.Thread}
self._background_tasks: Dict[str, threading.Thread] = {}
self._background_task_counter = 0
def _invalidate(self, min_interval: float = 0.25) -> None:
"""Throttled UI repaint — prevents terminal blinking on slow/SSH connections."""
import time as _time
@@ -2825,6 +2829,8 @@ class HermesCLI:
self._reload_mcp()
elif cmd_lower.startswith("/rollback"):
self._handle_rollback_command(cmd_original)
elif cmd_lower.startswith("/background"):
self._handle_background_command(cmd_original)
elif cmd_lower.startswith("/skin"):
self._handle_skin_command(cmd_original)
else:
@@ -2869,6 +2875,113 @@ class HermesCLI:
return True
def _handle_background_command(self, cmd: str):
"""Handle /background <prompt> — run a prompt in a separate background session.
Spawns a new AIAgent in a background thread with its own session.
When it completes, prints the result to the CLI without modifying
the active session's conversation history.
"""
parts = cmd.strip().split(maxsplit=1)
if len(parts) < 2 or not parts[1].strip():
_cprint(" Usage: /background <prompt>")
_cprint(" Example: /background Summarize the top HN stories today")
_cprint(" The task runs in a separate session and results display here when done.")
return
prompt = parts[1].strip()
self._background_task_counter += 1
task_num = self._background_task_counter
task_id = f"bg_{datetime.now().strftime('%H%M%S')}_{uuid.uuid4().hex[:6]}"
# Make sure we have valid credentials
if not self._ensure_runtime_credentials():
_cprint(" (>_<) Cannot start background task: no valid credentials.")
return
_cprint(f" 🔄 Background task #{task_num} started: \"{prompt[:60]}{'...' if len(prompt) > 60 else ''}\"")
_cprint(f" Task ID: {task_id}")
_cprint(f" You can continue chatting — results will appear when done.\n")
def run_background():
try:
bg_agent = AIAgent(
model=self.model,
api_key=self.api_key,
base_url=self.base_url,
provider=self.provider,
api_mode=self.api_mode,
max_iterations=self.max_turns,
enabled_toolsets=self.enabled_toolsets,
quiet_mode=True,
verbose_logging=False,
session_id=task_id,
platform="cli",
session_db=self._session_db,
reasoning_config=self.reasoning_config,
providers_allowed=self._providers_only,
providers_ignored=self._providers_ignore,
providers_order=self._providers_order,
provider_sort=self._provider_sort,
provider_require_parameters=self._provider_require_params,
provider_data_collection=self._provider_data_collection,
fallback_model=self._fallback_model,
)
result = bg_agent.run_conversation(
user_message=prompt,
task_id=task_id,
)
response = result.get("final_response", "") if result else ""
if not response and result and result.get("error"):
response = f"Error: {result['error']}"
# Display result in the CLI (thread-safe via patch_stdout)
print()
_cprint(f"{_GOLD}{'' * 40}{_RST}")
_cprint(f" ✅ Background task #{task_num} complete")
_cprint(f" Prompt: \"{prompt[:60]}{'...' if len(prompt) > 60 else ''}\"")
_cprint(f"{_GOLD}{'' * 40}{_RST}")
if response:
try:
from hermes_cli.skin_engine import get_active_skin
_skin = get_active_skin()
label = _skin.get_branding("response_label", "⚕ Hermes")
_resp_color = _skin.get_color("response_border", "#CD7F32")
except Exception:
label = "⚕ Hermes"
_resp_color = "#CD7F32"
_chat_console = ChatConsole()
_chat_console.print(Panel(
response,
title=f"[bold]{label} (background #{task_num})[/bold]",
title_align="left",
border_style=_resp_color,
box=rich_box.HORIZONTALS,
padding=(1, 2),
))
else:
_cprint(" (No response generated)")
# Play bell if enabled
if self.bell_on_complete:
sys.stdout.write("\a")
sys.stdout.flush()
except Exception as e:
print()
_cprint(f" ❌ Background task #{task_num} failed: {e}")
finally:
self._background_tasks.pop(task_id, None)
if self._app:
self._invalidate(min_interval=0)
thread = threading.Thread(target=run_background, daemon=True, name=f"bg-task-{task_id}")
self._background_tasks[task_id] = thread
thread.start()
def _handle_skin_command(self, cmd: str):
"""Handle /skin [name] — show or change the display skin."""
try: