refactor: codebase-wide lint cleanup — unused imports, dead code, and inefficient patterns (#5821)

Comprehensive cleanup across 80 files based on automated (ruff, pyflakes, vulture)
and manual analysis of the entire codebase.

Changes by category:

Unused imports removed (~95 across 55 files):
- Removed genuinely unused imports from all major subsystems
- agent/, hermes_cli/, tools/, gateway/, plugins/, cron/
- Includes imports in try/except blocks that were truly unused
  (vs availability checks which were left alone)

Unused variables removed (~25):
- Removed dead variables: connected, inner, channels, last_exc,
  source, new_server_names, verify, pconfig, default_terminal,
  result, pending_handled, temperature, loop
- Dropped unused argparse subparser assignments in hermes_cli/main.py
  (12 instances of add_parser() where result was never used)

Dead code removed:
- run_agent.py: Removed dead ternary (None if False else None) and
  surrounding unreachable branch in identity fallback
- run_agent.py: Removed write-only attribute _last_reported_tool
- hermes_cli/providers.py: Removed dead @property decorator on
  module-level function (decorator has no effect outside a class)
- gateway/run.py: Removed unused MCP config load before reconnect
- gateway/platforms/slack.py: Removed dead SessionSource construction

Undefined name bugs fixed (would cause NameError at runtime):
- batch_runner.py: Added missing logger = logging.getLogger(__name__)
- tools/environments/daytona.py: Added missing Dict and Path imports

Unnecessary global statements removed (14):
- tools/terminal_tool.py: 5 functions declared global for dicts
  they only mutated via .pop()/[key]=value (no rebinding)
- tools/browser_tool.py: cleanup thread loop only reads flag
- tools/rl_training_tool.py: 4 functions only do dict mutations
- tools/mcp_oauth.py: only reads the global
- hermes_time.py: only reads cached values

Inefficient patterns fixed:
- startswith/endswith tuple form: 15 instances of
  x.startswith('a') or x.startswith('b') consolidated to
  x.startswith(('a', 'b'))
- len(x)==0 / len(x)>0: 13 instances replaced with pythonic
  truthiness checks (not x / bool(x))
- in dict.keys(): 5 instances simplified to in dict
- Redefined unused name: removed duplicate _strip_mdv2 import in
  send_message_tool.py

Other fixes:
- hermes_cli/doctor.py: Replaced undefined logger.debug() with pass
- hermes_cli/config.py: Consolidated chained .endswith() calls

Test results: 3934 passed, 17 failed (all pre-existing on main),
19 skipped. Zero regressions.
This commit is contained in:
Teknium 2026-04-07 10:25:31 -07:00 committed by GitHub
parent afe6c63c52
commit d0ffb111c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
80 changed files with 81 additions and 210 deletions

View file

@ -15,7 +15,6 @@ Usage::
import asyncio
import logging
import os
import sys
from pathlib import Path
from hermes_constants import get_hermes_home

View file

@ -262,8 +262,6 @@ class SessionManager:
if self._db_instance is not None:
return self._db_instance
try:
import os
from pathlib import Path
from hermes_state import SessionDB
hermes_home = get_hermes_home()
self._db_instance = SessionDB(db_path=hermes_home / "state.db")

View file

@ -188,9 +188,7 @@ def _requires_bearer_auth(base_url: str | None) -> bool:
if not base_url:
return False
normalized = base_url.rstrip("/").lower()
return normalized.startswith("https://api.minimax.io/anthropic") or normalized.startswith(
"https://api.minimaxi.com/anthropic"
)
return normalized.startswith(("https://api.minimax.io/anthropic", "https://api.minimaxi.com/anthropic"))
def build_anthropic_client(api_key: str, base_url: str = None):
@ -847,7 +845,7 @@ def _convert_openai_image_part_to_anthropic(part: Dict[str, Any]) -> Optional[Di
},
}
if url.startswith("http://") or url.startswith("https://"):
if url.startswith(("http://", "https://")):
return {
"type": "image",
"source": {

View file

@ -209,7 +209,6 @@ class _CodexCompletionsAdapter:
def create(self, **kwargs) -> Any:
messages = kwargs.get("messages", [])
model = kwargs.get("model", self._model)
temperature = kwargs.get("temperature")
# Separate system/instructions from conversation messages.
# Convert chat.completions multimodal content blocks to Responses

View file

@ -13,7 +13,7 @@ from __future__ import annotations
import json
import logging
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List
from agent.memory_provider import MemoryProvider

View file

@ -10,21 +10,18 @@ import uuid
import os
import re
from dataclasses import dataclass, fields, replace
from datetime import datetime, timezone
from datetime import datetime
from typing import Any, Dict, List, Optional, Set, Tuple
from hermes_constants import OPENROUTER_BASE_URL
import hermes_cli.auth as auth_mod
from hermes_cli.auth import (
ACCESS_TOKEN_REFRESH_SKEW_SECONDS,
CODEX_ACCESS_TOKEN_REFRESH_SKEW_SECONDS,
DEFAULT_AGENT_KEY_MIN_TTL_SECONDS,
PROVIDER_REGISTRY,
_agent_key_is_usable,
_codex_access_token_is_expiring,
_decode_jwt_claims,
_import_codex_cli_tokens,
_is_expiring,
_load_auth_store,
_load_provider_state,
_resolve_zai_base_url,

View file

@ -34,7 +34,7 @@ from __future__ import annotations
import logging
from abc import ABC, abstractmethod
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List
logger = logging.getLogger(__name__)

View file

@ -23,9 +23,9 @@ import json
import logging
import os
import time
from dataclasses import dataclass, field
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple, Union
from typing import Any, Dict, List, Optional, Tuple
from utils import atomic_json_write
@ -231,7 +231,7 @@ def fetch_models_dev(force_refresh: bool = False) -> Dict[str, Any]:
response = requests.get(MODELS_DEV_URL, timeout=15)
response.raise_for_status()
data = response.json()
if isinstance(data, dict) and len(data) > 0:
if isinstance(data, dict) and data:
_models_dev_cache = data
_models_dev_cache_time = time.time()
_save_disk_cache(data)

View file

@ -10,7 +10,7 @@ import os
import re
import sys
from pathlib import Path
from typing import Any, Dict, List, Optional, Set, Tuple
from typing import Any, Dict, List, Set, Tuple
from hermes_constants import get_hermes_home

View file

@ -15,7 +15,6 @@ Inspired by Block/goose's SubdirectoryHintTracker.
import logging
import os
import re
import shlex
from pathlib import Path
from typing import Dict, Any, Optional, Set

View file

@ -31,6 +31,8 @@ from multiprocessing import Pool, Lock
import traceback
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRemainingColumn, MofNCompleteColumn
from rich.console import Console
logger = logging.getLogger(__name__)
import fire
from run_agent import AIAgent
@ -1016,7 +1018,7 @@ class BatchRunner:
tool_stats = data.get('tool_stats', {})
# Check for invalid tool names (model hallucinations)
invalid_tools = [k for k in tool_stats.keys() if k not in VALID_TOOLS]
invalid_tools = [k for k in tool_stats if k not in VALID_TOOLS]
if invalid_tools:
filtered_entries += 1

6
cli.py
View file

@ -70,7 +70,7 @@ _COMMAND_SPINNER_FRAMES = ("⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧
# Load .env from ~/.hermes/.env first, then project root as dev fallback.
# User-managed env files should override stale shell exports on restart.
from hermes_constants import get_hermes_home, display_hermes_home, OPENROUTER_BASE_URL
from hermes_constants import get_hermes_home, display_hermes_home
from hermes_cli.env_loader import load_hermes_dotenv
_hermes_home = get_hermes_home()
@ -4246,7 +4246,6 @@ class HermesCLI:
try:
config = load_gateway_config()
connected = config.get_connected_platforms()
print(" Messaging Platform Configuration:")
print(" " + "-" * 55)
@ -6008,7 +6007,7 @@ class HermesCLI:
timeout = CLI_CONFIG.get("clarify", {}).get("timeout", 120)
response_queue = queue.Queue()
is_open_ended = not choices or len(choices) == 0
is_open_ended = not choices
self._clarify_state = {
"question": question,
@ -7839,7 +7838,6 @@ class HermesCLI:
title = '🔐 Sudo Password Required'
body = 'Enter password below (hidden), or press Enter to skip'
box_width = _panel_box_width(title, [body])
inner = max(0, box_width - 2)
lines = []
lines.append(('class:sudo-border', '╭─ '))
lines.append(('class:sudo-title', title))

View file

@ -25,7 +25,6 @@ except ImportError:
import msvcrt
except ImportError:
msvcrt = None
import time
from pathlib import Path
from typing import Optional

View file

@ -124,7 +124,6 @@ def _build_discord(adapter) -> List[Dict[str, str]]:
def _build_slack(adapter) -> List[Dict[str, str]]:
"""List Slack channels the bot has joined."""
channels = []
# Slack adapter may expose a web client
client = getattr(adapter, "_app", None) or getattr(adapter, "_client", None)
if not client:

View file

@ -27,7 +27,6 @@ sys.path.insert(0, str(_Path(__file__).resolve().parents[2]))
from gateway.config import Platform, PlatformConfig
from gateway.session import SessionSource, build_session_key
from hermes_cli.config import get_hermes_home
from hermes_constants import get_hermes_dir

View file

@ -60,7 +60,6 @@ try:
CreateMessageRequestBody,
GetChatRequest,
GetMessageRequest,
GetImageRequest,
GetMessageResourceRequest,
P2ImMessageMessageReadV1,
ReplyMessageRequest,

View file

@ -1057,7 +1057,7 @@ class MatrixAdapter(BasePlatformAdapter):
# Message type.
msg_type = MessageType.TEXT
if body.startswith("!") or body.startswith("/"):
if body.startswith(("!", "/")):
msg_type = MessageType.COMMAND
source = self.build_source(

View file

@ -430,7 +430,6 @@ class MattermostAdapter(BasePlatformAdapter):
ct = resp.content_type or "application/octet-stream"
break
except (aiohttp.ClientError, asyncio.TimeoutError) as exc:
last_exc = exc
if attempt < 2:
await asyncio.sleep(1.5 * (attempt + 1))
continue

View file

@ -979,15 +979,6 @@ class SlackAdapter(BasePlatformAdapter):
try:
# Build a SessionSource for this thread
from gateway.session import SessionSource
from gateway.config import Platform
source = SessionSource(
platform=Platform.SLACK,
chat_id=channel_id,
chat_type="group",
user_id=user_id,
thread_id=thread_ts,
)
# Generate the session key using the same logic as SessionStore
# This mirrors the logic in build_session_key for group sessions

View file

@ -1369,7 +1369,7 @@ class TelegramAdapter(BasePlatformAdapter):
with open(audio_path, "rb") as audio_file:
# .ogg files -> send as voice (round playable bubble)
if audio_path.endswith(".ogg") or audio_path.endswith(".opus"):
if audio_path.endswith((".ogg", ".opus")):
_voice_thread = metadata.get("thread_id") if metadata else None
msg = await self._bot.send_voice(
chat_id=int(chat_id),

View file

@ -653,7 +653,7 @@ class WeComAdapter(BasePlatformAdapter):
return ".png"
if data.startswith(b"\xff\xd8\xff"):
return ".jpg"
if data.startswith(b"GIF87a") or data.startswith(b"GIF89a"):
if data.startswith((b"GIF87a", b"GIF89a")):
return ".gif"
if data.startswith(b"RIFF") and data[8:12] == b"WEBP":
return ".webp"
@ -689,7 +689,7 @@ class WeComAdapter(BasePlatformAdapter):
@staticmethod
def _derive_message_type(body: Dict[str, Any], text: str, media_types: List[str]) -> MessageType:
"""Choose the normalized inbound message type."""
if any(mtype.startswith("application/") or mtype.startswith("text/") for mtype in media_types):
if any(mtype.startswith(("application/", "text/")) for mtype in media_types):
return MessageType.DOCUMENT
if any(mtype.startswith("image/") for mtype in media_types):
return MessageType.TEXT if text else MessageType.PHOTO

View file

@ -27,7 +27,6 @@ _IS_WINDOWS = platform.system() == "Windows"
from pathlib import Path
from typing import Dict, Optional, Any
from hermes_cli.config import get_hermes_home
from hermes_constants import get_hermes_dir
logger = logging.getLogger(__name__)

View file

@ -24,7 +24,6 @@ import signal
import tempfile
import threading
import time
import uuid
from pathlib import Path
from datetime import datetime
from typing import Dict, Optional, Any, List
@ -378,7 +377,7 @@ def _check_unavailable_skill(command_name: str) -> str | None:
)
# Check optional skills (shipped with repo but not installed)
from hermes_constants import get_hermes_home, get_optional_skills_dir
from hermes_constants import get_optional_skills_dir
repo_root = Path(__file__).resolve().parent.parent
optional_dir = get_optional_skills_dir(repo_root / "optional-skills")
if optional_dir.exists():
@ -2822,7 +2821,7 @@ class GatewayRunner:
guessed, _ = _mimetypes.guess_type(path)
if guessed:
mtype = guessed
if not (mtype.startswith("application/") or mtype.startswith("text/")):
if not mtype.startswith(("application/", "text/")):
continue
# Extract display filename by stripping the doc_{uuid12}_ prefix
import os as _os
@ -3909,7 +3908,7 @@ class GatewayRunner:
return f"🎭 Personality set to **{args}**\n_(takes effect on next message)_"
available = "`none`, " + ", ".join(f"`{n}`" for n in personalities.keys())
available = "`none`, " + ", ".join(f"`{n}`" for n in personalities)
return f"Unknown personality: `{args}`\n\nAvailable: {available}"
async def _handle_retry_command(self, event: MessageEvent) -> str:
@ -5321,9 +5320,6 @@ class GatewayRunner:
old_servers = set(_servers.keys())
# Read new config before shutting down, so we know what will be added/removed
new_config = _load_mcp_config()
new_server_names = set(new_config.keys())
# Shutdown existing connections
await loop.run_in_executor(None, shutdown_mcp_servers)
@ -5411,7 +5407,6 @@ class GatewayRunner:
from tools.approval import (
resolve_gateway_approval, has_blocking_approval,
pending_approval_count,
)
if not has_blocking_approval(session_key):

View file

@ -128,7 +128,7 @@ class GatewayStreamConsumer:
got_done
or got_segment_break
or (elapsed >= self.cfg.edit_interval
and len(self._accumulated) > 0)
and self._accumulated)
or len(self._accumulated) >= self.cfg.buffer_threshold
)

View file

@ -2839,7 +2839,6 @@ def _login_nous(args, pconfig: ProviderConfig) -> None:
)
inference_base_url = auth_state["inference_base_url"]
verify: bool | str = False if insecure else (ca_bundle if ca_bundle else True)
with _auth_store_lock():
auth_store = _load_auth_store()

View file

@ -18,7 +18,6 @@ from agent.credential_pool import (
STRATEGY_ROUND_ROBIN,
STRATEGY_RANDOM,
STRATEGY_LEAST_USED,
SUPPORTED_POOL_STRATEGIES,
PooledCredential,
_exhausted_until,
_normalize_custom_pool_name,

View file

@ -5,7 +5,6 @@ Pure display functions with no HermesCLI state dependency.
import json
import logging
import os
import shutil
import subprocess
import threading

View file

@ -25,7 +25,7 @@ def clarify_callback(cli, question, choices):
timeout = CLI_CONFIG.get("clarify", {}).get("timeout", 120)
response_queue = queue.Queue()
is_open_ended = not choices or len(choices) == 0
is_open_ended = not choices
cli._clarify_state = {
"question": question,

View file

@ -10,7 +10,6 @@ Usage:
import importlib.util
import logging
import shutil
import sys
from datetime import datetime
from pathlib import Path
@ -24,7 +23,6 @@ from hermes_cli.setup import (
print_info,
print_success,
print_error,
print_warning,
prompt_yes_no,
)

View file

@ -2520,7 +2520,7 @@ def set_config_value(key: str, value: str):
'TINKER_API_KEY',
]
if key.upper() in api_keys or key.upper().endswith('_API_KEY') or key.upper().endswith('_TOKEN') or key.upper().startswith('TERMINAL_SSH'):
if key.upper() in api_keys or key.upper().endswith(('_API_KEY', '_TOKEN')) or key.upper().startswith('TERMINAL_SSH'):
save_env_value(key.upper(), value)
print(f"✓ Set {key} in {get_env_path()}")
return

View file

@ -920,8 +920,8 @@ def run_doctor(args):
pass
except ImportError:
pass
except Exception as _e:
logger.debug("Profile health check failed: %s", _e)
except Exception:
pass
# =========================================================================
# Summary

View file

@ -15,7 +15,6 @@ Usage examples::
hermes logs --since 30m -f # follow, starting 30 min ago
"""
import os
import re
import sys
import time

View file

@ -1154,7 +1154,7 @@ def _model_flow_nous(config, current_model="", args=None):
from hermes_cli.auth import (
get_provider_auth_state, _prompt_model_selection, _save_model_choice,
_update_config_for_provider, resolve_nous_runtime_credentials,
fetch_nous_models, AuthError, format_auth_error,
AuthError, format_auth_error,
_login_nous, PROVIDER_REGISTRY,
)
from hermes_cli.config import get_env_value, save_config, save_env_value
@ -1314,7 +1314,6 @@ def _model_flow_openai_codex(config, current_model=""):
PROVIDER_REGISTRY, DEFAULT_CODEX_BASE_URL,
)
from hermes_cli.codex_models import get_codex_model_ids
from hermes_cli.config import get_env_value, save_env_value
import argparse
status = get_codex_auth_status()
@ -1367,7 +1366,7 @@ def _model_flow_custom(config):
so it appears in the provider menu on subsequent runs.
"""
from hermes_cli.auth import _save_model_choice, deactivate_provider
from hermes_cli.config import get_env_value, save_env_value, load_config, save_config
from hermes_cli.config import get_env_value, load_config, save_config
current_url = get_env_value("OPENAI_BASE_URL") or ""
current_key = get_env_value("OPENAI_API_KEY") or ""
@ -1629,7 +1628,7 @@ def _model_flow_named_custom(config, provider_info):
Otherwise probes the endpoint's /models API to let the user pick one.
"""
from hermes_cli.auth import _save_model_choice, deactivate_provider
from hermes_cli.config import save_env_value, load_config, save_config
from hermes_cli.config import load_config, save_config
from hermes_cli.models import fetch_api_models
name = provider_info["name"]
@ -1839,7 +1838,7 @@ def _model_flow_copilot(config, current_model=""):
deactivate_provider,
resolve_api_key_provider_credentials,
)
from hermes_cli.config import get_env_value, save_env_value, load_config, save_config
from hermes_cli.config import save_env_value, load_config, save_config
from hermes_cli.models import (
fetch_api_models,
fetch_github_model_catalog,
@ -2430,8 +2429,6 @@ def _model_flow_anthropic(config, current_model=""):
)
from hermes_cli.models import _PROVIDER_MODELS
pconfig = PROVIDER_REGISTRY["anthropic"]
# Check ALL credential sources
existing_key = (
get_env_value("ANTHROPIC_TOKEN")
@ -3700,7 +3697,7 @@ def cmd_update(args):
try:
from hermes_cli.gateway import (
is_macos, is_linux, _ensure_user_systemd_env,
get_systemd_linger_status, find_gateway_pids,
find_gateway_pids,
_get_service_pids,
)
import signal as _signal
@ -3856,7 +3853,7 @@ def cmd_profile(args):
"""Profile management — create, delete, list, switch, alias."""
from hermes_cli.profiles import (
list_profiles, create_profile, delete_profile, seed_profile_skills,
get_active_profile, set_active_profile, get_active_profile_name,
set_active_profile, get_active_profile_name,
check_alias_collision, create_wrapper_script, remove_wrapper_script,
_is_wrapper_dir_in_path, _get_wrapper_dir,
)
@ -3984,7 +3981,6 @@ def cmd_profile(args):
print(f" {name} chat Start chatting")
print(f" {name} gateway start Start the messaging gateway")
if clone or clone_all:
from hermes_constants import get_hermes_home
profile_dir_display = f"~/.hermes/profiles/{name}"
print(f"\n Edit {profile_dir_display}/.env for different API keys")
print(f" Edit {profile_dir_display}/SOUL.md for different personality")
@ -4407,7 +4403,7 @@ For more help on a command:
gateway_uninstall.add_argument("--system", action="store_true", help="Target the Linux system-level gateway service")
# gateway setup
gateway_setup = gateway_subparsers.add_parser("setup", help="Configure messaging platforms")
gateway_subparsers.add_parser("setup", help="Configure messaging platforms")
gateway_parser.set_defaults(func=cmd_gateway)
@ -4682,10 +4678,10 @@ For more help on a command:
config_subparsers = config_parser.add_subparsers(dest="config_command")
# config show (default)
config_show = config_subparsers.add_parser("show", help="Show current configuration")
config_subparsers.add_parser("show", help="Show current configuration")
# config edit
config_edit = config_subparsers.add_parser("edit", help="Open config file in editor")
config_subparsers.add_parser("edit", help="Open config file in editor")
# config set
config_set = config_subparsers.add_parser("set", help="Set a configuration value")
@ -4693,16 +4689,16 @@ For more help on a command:
config_set.add_argument("value", nargs="?", help="Value to set")
# config path
config_path = config_subparsers.add_parser("path", help="Print config file path")
config_subparsers.add_parser("path", help="Print config file path")
# config env-path
config_env = config_subparsers.add_parser("env-path", help="Print .env file path")
config_subparsers.add_parser("env-path", help="Print .env file path")
# config check
config_check = config_subparsers.add_parser("check", help="Check for missing/outdated config")
config_subparsers.add_parser("check", help="Check for missing/outdated config")
# config migrate
config_migrate = config_subparsers.add_parser("migrate", help="Update config with new options")
config_subparsers.add_parser("migrate", help="Update config with new options")
config_parser.set_defaults(func=cmd_config)
@ -4716,7 +4712,7 @@ For more help on a command:
)
pairing_sub = pairing_parser.add_subparsers(dest="pairing_action")
pairing_list_parser = pairing_sub.add_parser("list", help="Show pending + approved users")
pairing_sub.add_parser("list", help="Show pending + approved users")
pairing_approve_parser = pairing_sub.add_parser("approve", help="Approve a pairing code")
pairing_approve_parser.add_argument("platform", help="Platform name (telegram, discord, slack, whatsapp)")
@ -4726,7 +4722,7 @@ For more help on a command:
pairing_revoke_parser.add_argument("platform", help="Platform name")
pairing_revoke_parser.add_argument("user_id", help="User ID to revoke")
pairing_clear_parser = pairing_sub.add_parser("clear-pending", help="Clear all pending codes")
pairing_sub.add_parser("clear-pending", help="Clear all pending codes")
def cmd_pairing(args):
from hermes_cli.pairing import pairing_command
@ -4902,7 +4898,7 @@ For more help on a command:
memory_sub = memory_parser.add_subparsers(dest="memory_command")
memory_sub.add_parser("setup", help="Interactive provider selection and configuration")
memory_sub.add_parser("status", help="Show current memory provider config")
memory_off_p = memory_sub.add_parser("off", help="Disable external provider (built-in only)")
memory_sub.add_parser("off", help="Disable external provider (built-in only)")
def cmd_memory(args):
sub = getattr(args, "memory_command", None)
@ -5066,7 +5062,7 @@ For more help on a command:
sessions_prune.add_argument("--source", help="Only prune sessions from this source")
sessions_prune.add_argument("--yes", "-y", action="store_true", help="Skip confirmation")
sessions_stats = sessions_subparsers.add_parser("stats", help="Show session store statistics")
sessions_subparsers.add_parser("stats", help="Show session store statistics")
sessions_rename = sessions_subparsers.add_parser("rename", help="Set or change a session's title")
sessions_rename.add_argument("session_id", help="Session ID to rename")
@ -5426,7 +5422,7 @@ For more help on a command:
)
profile_subparsers = profile_parser.add_subparsers(dest="profile_action")
profile_list = profile_subparsers.add_parser("list", help="List all profiles")
profile_subparsers.add_parser("list", help="List all profiles")
profile_use = profile_subparsers.add_parser("use", help="Set sticky default profile")
profile_use.add_argument("profile_name", help="Profile name (or 'default')")

View file

@ -21,22 +21,16 @@ OpenRouter variant suffixes (``:free``, ``:extended``, ``:fast``).
from __future__ import annotations
import logging
from dataclasses import dataclass, field
from dataclasses import dataclass
from typing import List, NamedTuple, Optional
from hermes_cli.providers import (
ALIASES,
LABELS,
TRANSPORT_TO_API_MODE,
determine_api_mode,
get_label,
get_provider,
is_aggregator,
normalize_provider,
resolve_provider_full,
)
from hermes_cli.model_normalize import (
detect_vendor,
normalize_model_for_provider,
)
from agent.models_dev import (

View file

@ -294,7 +294,7 @@ def cmd_install(identifier: str, force: bool = False) -> None:
sys.exit(1)
# Warn about insecure / local URL schemes
if git_url.startswith("http://") or git_url.startswith("file://"):
if git_url.startswith(("http://", "file://")):
console.print(
"[yellow]Warning:[/yellow] Using insecure/local URL scheme. "
"Consider using https:// or git@ for production installs."

View file

@ -26,7 +26,7 @@ import shutil
import stat
import subprocess
import sys
from dataclasses import dataclass, field
from dataclasses import dataclass
from pathlib import Path, PurePosixPath, PureWindowsPath
from typing import List, Optional
@ -517,7 +517,6 @@ def delete_profile(name: str, yes: bool = False) -> Path:
]
# Check for service
from hermes_cli.gateway import _profile_suffix, get_service_name
wrapper_path = _get_wrapper_dir() / name
has_wrapper = wrapper_path.exists()
if has_wrapper:

View file

@ -20,8 +20,7 @@ Other modules import from this file. No parallel registries.
from __future__ import annotations
import logging
import os
from dataclasses import dataclass, field
from dataclasses import dataclass
from typing import Any, Dict, List, Optional, Tuple
logger = logging.getLogger(__name__)
@ -357,14 +356,6 @@ def _build_labels() -> Dict[str, str]:
# Lazy-built on first access
_labels_cache: Optional[Dict[str, str]] = None
@property
def LABELS() -> Dict[str, str]:
"""Backward-compatible labels dict."""
global _labels_cache
if _labels_cache is None:
_labels_cache = _build_labels()
return _labels_cache
# For direct import compat, expose as module-level dict
# Built on demand by get_label() calls
LABELS: Dict[str, str] = {

View file

@ -21,7 +21,6 @@ from typing import Optional, Dict, Any
from hermes_cli.nous_subscription import (
apply_nous_provider_defaults,
get_nous_subscription_explainer_lines,
get_nous_subscription_features,
)
from tools.tool_backend_helpers import managed_nous_tools_enabled
@ -1348,8 +1347,6 @@ def setup_terminal_backend(config: dict):
terminal_choices.append(f"Keep current ({current_backend})")
idx_to_backend[keep_current_idx] = current_backend
default_terminal = backend_to_idx.get(current_backend, 0)
terminal_idx = prompt_choice(
"Select terminal backend:", terminal_choices, keep_current_idx
)

View file

@ -96,7 +96,6 @@ Activate with ``/skin <name>`` in the CLI or ``display.skin: <name>`` in config.
"""
import logging
import os
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple

View file

@ -6,7 +6,6 @@ Provides options for:
- Keep data: Remove code but keep ~/.hermes/ (configs, sessions, logs)
"""
import os
import shutil
import subprocess
from pathlib import Path

View file

@ -16,7 +16,7 @@ import re
import secrets
import time
from pathlib import Path
from typing import Dict, Optional
from typing import Dict
from hermes_constants import display_hermes_home

View file

@ -13,7 +13,6 @@ secrets are never written to disk.
"""
import logging
import os
from logging.handlers import RotatingFileHandler
from pathlib import Path
from typing import Optional

View file

@ -16,7 +16,6 @@ Key design decisions:
import json
import logging
import os
import random
import re
import sqlite3

View file

@ -16,7 +16,6 @@ crashes due to a bad timezone string.
import logging
import os
from datetime import datetime
from pathlib import Path
from hermes_constants import get_hermes_home
from typing import Optional
@ -92,7 +91,6 @@ def get_timezone() -> Optional[ZoneInfo]:
def get_timezone_name() -> str:
"""Return the IANA name of the configured timezone, or empty string."""
global _cached_tz_name, _cache_resolved
if not _cache_resolved:
get_timezone() # populates cache
return _cached_tz_name or ""

View file

@ -37,9 +37,8 @@ import sys
import threading
import time
from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Optional
from typing import Dict, List, Optional
logger = logging.getLogger("hermes.mcp_serve")

View file

@ -23,7 +23,6 @@ import os
import shutil
import subprocess
import threading
import time
from pathlib import Path
from typing import Any, Dict, List, Optional

View file

@ -20,7 +20,6 @@ from __future__ import annotations
import json
import logging
import re
from pathlib import Path
from typing import Any, Dict, List
from agent.memory_provider import MemoryProvider

View file

@ -6,7 +6,6 @@ Single-user Hermes memory store plugin.
import re
import sqlite3
import threading
from datetime import datetime
from pathlib import Path
try:

View file

@ -18,7 +18,6 @@ from __future__ import annotations
import json
import logging
import threading
from pathlib import Path
from typing import Any, Dict, List, Optional
from agent.memory_provider import MemoryProvider

View file

@ -11,7 +11,7 @@ import sys
from pathlib import Path
from hermes_constants import get_hermes_home
from plugins.memory.honcho.client import resolve_active_host, resolve_config_path, GLOBAL_CONFIG_PATH, HOST
from plugins.memory.honcho.client import resolve_active_host, resolve_config_path, HOST
def clone_honcho_for_profile(profile_name: str) -> bool:
@ -1220,7 +1220,6 @@ def register_cli(subparser) -> None:
Called by the plugin CLI registration system during argparse setup.
The *subparser* is the parser for ``hermes honcho``.
"""
import argparse
subparser.add_argument(
"--target-profile", metavar="NAME", dest="target_profile",

View file

@ -20,7 +20,6 @@ import logging
import os
import threading
import time
from pathlib import Path
from typing import Any, Dict, List
from agent.memory_provider import MemoryProvider

View file

@ -20,7 +20,6 @@ Config (env vars or hermes config.yaml under retaindb:):
from __future__ import annotations
import hashlib
import json
import logging
import os
@ -189,7 +188,7 @@ class _Client:
"Content-Type": "application/json",
"x-sdk-runtime": "hermes-plugin",
}
if path.startswith("/v1/memory") or path.startswith("/v1/context"):
if path.startswith(("/v1/memory", "/v1/context")):
h["X-API-Key"] = token
return h

View file

@ -20,7 +20,6 @@ Usage:
response = agent.run_conversation("Tell me about the latest Python updates")
"""
import atexit
import asyncio
import base64
import concurrent.futures
@ -36,7 +35,6 @@ import sys
import tempfile
import time
import threading
import weakref
from types import SimpleNamespace
import uuid
from typing import List, Dict, Any, Optional
@ -654,7 +652,7 @@ class AIAgent:
self.stream_delta_callback = stream_delta_callback
self.status_callback = status_callback
self.tool_gen_callback = tool_gen_callback
self._last_reported_tool = None # Track for "new tool" mode
# Tool execution state — allows _vprint during tool execution
# even when stream consumers are registered (no tokens streaming then)
@ -2702,20 +2700,7 @@ class AIAgent:
if not _soul_loaded:
# Fallback to hardcoded identity
_ai_peer_name = (
None
if False
else None
)
if _ai_peer_name:
_identity = DEFAULT_AGENT_IDENTITY.replace(
"You are Hermes Agent",
f"You are {_ai_peer_name}",
1,
)
else:
_identity = DEFAULT_AGENT_IDENTITY
prompt_parts = [_identity]
prompt_parts = [DEFAULT_AGENT_IDENTITY]
# Tool-aware behavioral guidance: only inject when the tools are loaded
tool_guidance = []
@ -3400,7 +3385,7 @@ class AIAgent:
elif "stream" in api_kwargs:
raise ValueError("Codex Responses stream flag is only allowed in fallback streaming requests.")
unexpected = sorted(key for key in api_kwargs.keys() if key not in allowed_keys)
unexpected = sorted(key for key in api_kwargs if key not in allowed_keys)
if unexpected:
raise ValueError(
f"Codex Responses request has unsupported field(s): {', '.join(unexpected)}."
@ -5908,7 +5893,7 @@ class AIAgent:
args = json.loads(tc.function.arguments)
flush_target = args.get("target", "memory")
from tools.memory_tool import memory_tool as _memory_tool
result = _memory_tool(
_memory_tool(
action=args.get("action"),
target=flush_target,
content=args.get("content"),
@ -7468,7 +7453,7 @@ class AIAgent:
elif not isinstance(output_items, list):
response_invalid = True
error_details.append("response.output is not a list")
elif len(output_items) == 0:
elif not output_items:
# If we reach here, _run_codex_stream's backfill
# from output_item.done events and text-delta
# synthesis both failed to populate output.
@ -7491,11 +7476,11 @@ class AIAgent:
elif not isinstance(content_blocks, list):
response_invalid = True
error_details.append("response.content is not a list")
elif len(content_blocks) == 0:
elif not content_blocks:
response_invalid = True
error_details.append("response.content is empty")
else:
if response is None or not hasattr(response, 'choices') or response.choices is None or len(response.choices) == 0:
if response is None or not hasattr(response, 'choices') or response.choices is None or not response.choices:
response_invalid = True
if response is None:
error_details.append("response is None")
@ -9033,7 +9018,6 @@ class AIAgent:
"content": f"Error executing tool: {error_msg}",
}
messages.append(err_msg)
pending_handled = True
break
# Non-tool errors don't need a synthetic message injected.

View file

@ -21,8 +21,6 @@ Usage:
"""
import argparse
import json
import os
import re
import shutil
import subprocess

View file

@ -17,7 +17,6 @@ Usage:
import json
import random
import os
from pathlib import Path
from typing import List, Dict, Any, Tuple
import fire
@ -138,7 +137,6 @@ def sample_from_datasets(
List of sampled trajectory entries
"""
from multiprocessing import Pool
from functools import partial
random.seed(seed)

View file

@ -12,7 +12,7 @@ Adapt this for your specific task by modifying:
import torch
import re
from datasets import load_dataset, Dataset
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig
from trl import GRPOTrainer, GRPOConfig

View file

@ -16,13 +16,10 @@ Usage in execute_code:
"""
import os
import sys
import json
import time
import re
import yaml
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
try:
from openai import OpenAI

View file

@ -20,7 +20,6 @@ Usage in execute_code:
import os
import re
import json
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
@ -404,7 +403,6 @@ def race_godmode_classic(query, api_key=None, timeout=60):
Each combo uses a different model paired with its best-performing jailbreak prompt.
Returns the best result across all combos.
"""
from collections import namedtuple
HALL_OF_FAME = [
{

View file

@ -17,7 +17,6 @@ Usage:
import re
import base64
import sys
# ═══════════════════════════════════════════════════════════════════
# Trigger words that commonly trip safety classifiers

View file

@ -27,9 +27,7 @@ import json
import logging
import os
import threading
import time
import uuid
from pathlib import Path
from typing import Any, Dict, Optional
import requests
@ -445,7 +443,7 @@ def camofox_get_images(task_id: Optional[str] = None) -> str:
lines = snapshot.split("\n")
for i, line in enumerate(lines):
stripped = line.strip()
if stripped.startswith("- img ") or stripped.startswith("img "):
if stripped.startswith(("- img ", "img ")):
alt_match = re.search(r'img\s+"([^"]*)"', stripped)
alt = alt_match.group(1) if alt_match else ""
# Look for URL on the next line

View file

@ -191,7 +191,7 @@ def _resolve_cdp_override(cdp_url: str) -> str:
return raw
discovery_url = raw
if lowered.startswith("ws://") or lowered.startswith("wss://"):
if lowered.startswith(("ws://", "wss://")):
if raw.count(":") == 2 and raw.rstrip("/").rsplit(":", 1)[-1].isdigit() and "/" not in raw.split(":", 2)[-1]:
discovery_url = ("http://" if lowered.startswith("ws://") else "https://") + raw.split("://", 1)[1]
else:
@ -458,8 +458,6 @@ def _browser_cleanup_thread_worker():
Runs every 30 seconds and checks for sessions that haven't been used
within the BROWSER_SESSION_INACTIVITY_TIMEOUT period.
"""
global _cleanup_running
while _cleanup_running:
try:
_cleanup_inactive_browser_sessions()

View file

@ -693,7 +693,6 @@ def _execute_remote(
the remote environment, and tool calls are proxied through a polling
thread that communicates via request/response files.
"""
from tools.terminal_tool import _interrupt_event
_cfg = _load_config()
timeout = _cfg.get("timeout", DEFAULT_TIMEOUT)

View file

@ -150,7 +150,6 @@ def _validate_cron_script_path(script: Optional[str]) -> Optional[str]:
if not script or not script.strip():
return None # empty/None = clearing the field, always OK
from pathlib import Path
from hermes_constants import get_hermes_home
raw = script.strip()

View file

@ -26,7 +26,6 @@ import json
import logging
import os
import uuid
from pathlib import Path
from typing import Any, Dict
from hermes_constants import get_hermes_home

View file

@ -12,7 +12,8 @@ import shlex
import threading
import uuid
import warnings
from typing import Optional
from pathlib import Path
from typing import Dict, Optional
from tools.environments.base import BaseEnvironment
from tools.interrupt import is_interrupted

View file

@ -11,11 +11,10 @@ import os
import shlex
import shutil
import subprocess
import tempfile
import threading
import uuid
from pathlib import Path
from typing import Any, Dict, Optional
from typing import Dict, Optional
from hermes_constants import get_hermes_home
from tools.environments.base import BaseEnvironment

View file

@ -43,7 +43,7 @@ import threading
import webbrowser
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
from typing import Any, Optional
from typing import Any
from urllib.parse import parse_qs, urlparse
logger = logging.getLogger(__name__)
@ -54,7 +54,7 @@ logger = logging.getLogger(__name__)
_OAUTH_AVAILABLE = False
try:
from mcp.client.auth import OAuthClientProvider, TokenStorage
from mcp.client.auth import OAuthClientProvider
from mcp.shared.auth import (
OAuthClientInformationFull,
OAuthClientMetadata,
@ -320,7 +320,6 @@ async def _wait_for_callback() -> tuple[str, str | None]:
OAuthNonInteractiveError: If the callback times out (no user present
to complete the browser auth).
"""
global _oauth_port
assert _oauth_port is not None, "OAuth callback port not set"
# The callback server is already running (started in build_oauth_auth).

View file

@ -260,7 +260,7 @@ class MemoryStore:
entries = self._entries_for(target)
matches = [(i, e) for i, e in enumerate(entries) if old_text in e]
if len(matches) == 0:
if not matches:
return {"success": False, "error": f"No entry matched '{old_text}'."}
if len(matches) > 1:
@ -310,7 +310,7 @@ class MemoryStore:
entries = self._entries_for(target)
matches = [(i, e) for i, e in enumerate(entries) if old_text in e]
if len(matches) == 0:
if not matches:
return {"success": False, "error": f"No entry matched '{old_text}'."}
if len(matches) > 1:

View file

@ -567,7 +567,7 @@ async def rl_select_environment(name: str) -> str:
TIP: Read the returned file_path to understand how the environment works.
"""
global _current_env, _current_config, _env_config_cache
global _current_env, _current_config
_initialize_environments()
@ -673,8 +673,6 @@ async def rl_edit_config(field: str, value: Any) -> str:
Returns:
JSON string with updated config or error message
"""
global _current_config
if not _current_env:
return json.dumps({
"error": "No environment selected. Use rl_select_environment(name) first.",
@ -727,8 +725,6 @@ async def rl_start_training() -> str:
Returns:
JSON string with run_id and initial status
"""
global _active_runs
if not _current_env:
return json.dumps({
"error": "No environment selected. Use rl_select_environment(name) first.",
@ -829,8 +825,6 @@ async def rl_check_status(run_id: str) -> str:
Returns:
JSON string with run status and metrics
"""
global _last_status_check
# Check rate limiting
now = time.time()
if run_id in _last_status_check:
@ -1311,7 +1305,7 @@ async def rl_test_inference(
"avg_accuracy": round(
sum(m.get("accuracy", 0) for m in working_models) / len(working_models), 3
) if working_models else 0,
"environment_working": len(working_models) > 0,
"environment_working": bool(working_models),
"output_directory": str(test_output_dir),
}

View file

@ -432,7 +432,7 @@ async def _send_telegram(token, chat_id, message, media_files=None, thread_id=No
else:
# Reuse the gateway adapter's format_message for markdown→MarkdownV2
try:
from gateway.platforms.telegram import TelegramAdapter, _strip_mdv2
from gateway.platforms.telegram import TelegramAdapter
_adapter = TelegramAdapter.__new__(TelegramAdapter)
formatted = _adapter.format_message(message)
except Exception:

View file

@ -430,7 +430,7 @@ class GitHubSource(SkillSource):
continue
dir_name = entry["name"]
if dir_name.startswith(".") or dir_name.startswith("_"):
if dir_name.startswith((".", "_")):
continue
prefix = path.rstrip("/")
@ -1163,7 +1163,7 @@ class SkillsShSource(SkillSource):
if entry.get("type") != "dir":
continue
dir_name = entry["name"]
if dir_name.startswith(".") or dir_name.startswith("_"):
if dir_name.startswith((".", "_")):
continue
if dir_name in ("skills", ".agents", ".claude"):
continue # already tried
@ -1382,7 +1382,7 @@ class ClawHubSource(SkillSource):
if isinstance(tags, list):
return [str(t) for t in tags]
if isinstance(tags, dict):
return [str(k) for k in tags.keys() if str(k) != "latest"]
return [str(k) for k in tags if str(k) != "latest"]
return []
@staticmethod

View file

@ -72,12 +72,10 @@ import logging
from hermes_constants import get_hermes_home
import os
import re
import sys
from enum import Enum
from pathlib import Path
from typing import Dict, Any, List, Optional, Set, Tuple
import yaml
from tools.registry import registry
logger = logging.getLogger(__name__)

View file

@ -720,8 +720,6 @@ def _create_environment(env_type: str, image: str, cwd: str, timeout: int,
def _cleanup_inactive_envs(lifetime_seconds: int = 300):
"""Clean up environments that have been inactive for longer than lifetime_seconds."""
global _active_environments, _last_activity
current_time = time.time()
# Check the process registry -- skip cleanup for sandboxes with active
@ -784,8 +782,6 @@ def _cleanup_inactive_envs(lifetime_seconds: int = 300):
def _cleanup_thread_worker():
"""Background thread worker that periodically cleans up inactive environments."""
global _cleanup_running
while _cleanup_running:
try:
config = _get_env_config()
@ -831,7 +827,7 @@ def get_active_environments_info() -> Dict[str, Any]:
# Calculate total disk usage (per-task to avoid double-counting)
total_size = 0
for task_id in _active_environments.keys():
for task_id in _active_environments:
scratch_dir = _get_scratch_dir()
pattern = f"hermes-*{task_id[:8]}*"
import glob
@ -848,8 +844,6 @@ def get_active_environments_info() -> Dict[str, Any]:
def cleanup_all_environments():
"""Clean up ALL active environments. Use with caution."""
global _active_environments, _last_activity
task_ids = list(_active_environments.keys())
cleaned = 0
@ -877,8 +871,6 @@ def cleanup_all_environments():
def cleanup_vm(task_id: str):
"""Manually clean up a specific environment by task_id."""
global _active_environments, _last_activity
# Remove from tracking dicts while holding the lock, but defer the
# actual (potentially slow) env.cleanup() call to outside the lock
# so other tool calls aren't blocked.
@ -1043,8 +1035,6 @@ def terminal_tool(
# Force run after user confirmation
# Note: force parameter is internal only, not exposed to model API
"""
global _active_environments, _last_activity
try:
# Get configuration
config = _get_env_config()

View file

@ -85,7 +85,7 @@ class TodoStore:
def has_items(self) -> bool:
"""Check if there are any items in the list."""
return len(self._items) > 0
return bool(self._items)
def format_for_injection(self) -> Optional[str]:
"""

View file

@ -550,7 +550,6 @@ def text_to_speech_tool(
if edge_available:
logger.info("Generating speech with Edge TTS...")
try:
loop = asyncio.get_running_loop()
import concurrent.futures
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
pool.submit(

View file

@ -82,7 +82,7 @@ def _validate_image_url(url: str) -> bool:
return False
# Basic HTTP/HTTPS URL check
if not (url.startswith("http://") or url.startswith("https://")):
if not url.startswith(("http://", "https://")):
return False
# Parse to ensure we at least have a network location; still allow URLs

View file

@ -108,7 +108,7 @@ def detect_audio_environment() -> dict:
)
return {
"available": len(warnings) == 0,
"available": not warnings,
"warnings": warnings,
"notices": notices,
}

View file

@ -12,7 +12,6 @@ from __future__ import annotations
import fnmatch
import logging
import os
import threading
import time
from pathlib import Path

View file

@ -592,7 +592,7 @@ def get_toolset_info(name: str) -> Dict[str, Any]:
"includes": toolset["includes"],
"resolved_tools": resolved_tools,
"tool_count": len(resolved_tools),
"is_composite": len(toolset["includes"]) > 0
"is_composite": bool(toolset["includes"])
}

View file

@ -32,7 +32,6 @@ Usage:
import json
import os
import re
import time
import yaml
import logging
@ -350,7 +349,6 @@ class TrajectoryCompressor:
which handles auth, headers, and provider detection internally.
For custom endpoints, falls back to raw client construction.
"""
from agent.auxiliary_client import call_llm, async_call_llm
provider = self._detect_provider()
if provider: