From d0ffb111c25d7be2287f824b8426a99f019aa58d Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Tue, 7 Apr 2026 10:25:31 -0700 Subject: [PATCH] =?UTF-8?q?refactor:=20codebase-wide=20lint=20cleanup=20?= =?UTF-8?q?=E2=80=94=20unused=20imports,=20dead=20code,=20and=20inefficien?= =?UTF-8?q?t=20patterns=20(#5821)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- acp_adapter/entry.py | 1 - acp_adapter/session.py | 2 - agent/anthropic_adapter.py | 6 +-- agent/auxiliary_client.py | 1 - agent/builtin_memory_provider.py | 2 +- agent/credential_pool.py | 5 +-- agent/memory_provider.py | 2 +- agent/models_dev.py | 6 +-- agent/skill_utils.py | 2 +- agent/subdirectory_hints.py | 1 - batch_runner.py | 4 +- cli.py | 6 +-- cron/scheduler.py | 1 - gateway/channel_directory.py | 1 - gateway/platforms/base.py | 1 - gateway/platforms/feishu.py | 1 - gateway/platforms/matrix.py | 2 +- gateway/platforms/mattermost.py | 1 - gateway/platforms/slack.py | 11 +---- gateway/platforms/telegram.py | 2 +- gateway/platforms/wecom.py | 4 +- gateway/platforms/whatsapp.py | 1 - gateway/run.py | 11 ++--- gateway/stream_consumer.py | 2 +- hermes_cli/auth.py | 1 - hermes_cli/auth_commands.py | 1 - hermes_cli/banner.py | 1 - hermes_cli/callbacks.py | 2 +- hermes_cli/claw.py | 2 - hermes_cli/config.py | 2 +- hermes_cli/doctor.py | 4 +- hermes_cli/logs.py | 1 - hermes_cli/main.py | 40 +++++++++---------- hermes_cli/model_switch.py | 8 +--- hermes_cli/plugins_cmd.py | 2 +- hermes_cli/profiles.py | 3 +- hermes_cli/providers.py | 11 +---- hermes_cli/setup.py | 3 -- hermes_cli/skin_engine.py | 1 - hermes_cli/uninstall.py | 1 - hermes_cli/webhook.py | 2 +- hermes_logging.py | 1 - hermes_state.py | 1 - hermes_time.py | 2 - mcp_serve.py | 3 +- plugins/memory/byterover/__init__.py | 1 - plugins/memory/holographic/__init__.py | 1 - plugins/memory/holographic/store.py | 1 - plugins/memory/honcho/__init__.py | 1 - plugins/memory/honcho/cli.py | 3 +- plugins/memory/mem0/__init__.py | 1 - plugins/memory/retaindb/__init__.py | 3 +- run_agent.py | 30 ++++---------- scripts/release.py | 2 - scripts/sample_and_compress.py | 2 - .../templates/basic_grpo_training.py | 2 +- .../godmode/scripts/auto_jailbreak.py | 3 -- .../godmode/scripts/godmode_race.py | 2 - .../godmode/scripts/parseltongue.py | 1 - tools/browser_camofox.py | 4 +- tools/browser_tool.py | 4 +- tools/code_execution_tool.py | 1 - tools/cronjob_tools.py | 1 - tools/debug_helpers.py | 1 - tools/environments/daytona.py | 3 +- tools/environments/singularity.py | 3 +- tools/mcp_oauth.py | 5 +-- tools/memory_tool.py | 4 +- tools/rl_training_tool.py | 10 +---- tools/send_message_tool.py | 2 +- tools/skills_hub.py | 6 +-- tools/skills_tool.py | 2 - tools/terminal_tool.py | 12 +----- tools/todo_tool.py | 2 +- tools/tts_tool.py | 1 - tools/vision_tools.py | 2 +- tools/voice_mode.py | 2 +- tools/website_policy.py | 1 - toolsets.py | 2 +- trajectory_compressor.py | 2 - 80 files changed, 81 insertions(+), 210 deletions(-) diff --git a/acp_adapter/entry.py b/acp_adapter/entry.py index 02e44c15e..7db5747a4 100644 --- a/acp_adapter/entry.py +++ b/acp_adapter/entry.py @@ -15,7 +15,6 @@ Usage:: import asyncio import logging -import os import sys from pathlib import Path from hermes_constants import get_hermes_home diff --git a/acp_adapter/session.py b/acp_adapter/session.py index b489c3984..4bb823987 100644 --- a/acp_adapter/session.py +++ b/acp_adapter/session.py @@ -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") diff --git a/agent/anthropic_adapter.py b/agent/anthropic_adapter.py index be2dec805..4dd3cadc0 100644 --- a/agent/anthropic_adapter.py +++ b/agent/anthropic_adapter.py @@ -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": { diff --git a/agent/auxiliary_client.py b/agent/auxiliary_client.py index 5f13994c8..35ba3c7ba 100644 --- a/agent/auxiliary_client.py +++ b/agent/auxiliary_client.py @@ -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 diff --git a/agent/builtin_memory_provider.py b/agent/builtin_memory_provider.py index df4e3b850..0d9cf6c06 100644 --- a/agent/builtin_memory_provider.py +++ b/agent/builtin_memory_provider.py @@ -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 diff --git a/agent/credential_pool.py b/agent/credential_pool.py index 144a91015..a47901c84 100644 --- a/agent/credential_pool.py +++ b/agent/credential_pool.py @@ -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, diff --git a/agent/memory_provider.py b/agent/memory_provider.py index 54ef1fb10..24593e334 100644 --- a/agent/memory_provider.py +++ b/agent/memory_provider.py @@ -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__) diff --git a/agent/models_dev.py b/agent/models_dev.py index 51eea8fe3..a23ce74b2 100644 --- a/agent/models_dev.py +++ b/agent/models_dev.py @@ -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) diff --git a/agent/skill_utils.py b/agent/skill_utils.py index f24163609..6b06a19e3 100644 --- a/agent/skill_utils.py +++ b/agent/skill_utils.py @@ -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 diff --git a/agent/subdirectory_hints.py b/agent/subdirectory_hints.py index a6ca2adc5..96903e2e2 100644 --- a/agent/subdirectory_hints.py +++ b/agent/subdirectory_hints.py @@ -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 diff --git a/batch_runner.py b/batch_runner.py index ed00665ea..32cd203b2 100644 --- a/batch_runner.py +++ b/batch_runner.py @@ -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 diff --git a/cli.py b/cli.py index a60f699d0..69a9e8e9c 100644 --- a/cli.py +++ b/cli.py @@ -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)) diff --git a/cron/scheduler.py b/cron/scheduler.py index 5f3feba07..f694f4407 100644 --- a/cron/scheduler.py +++ b/cron/scheduler.py @@ -25,7 +25,6 @@ except ImportError: import msvcrt except ImportError: msvcrt = None -import time from pathlib import Path from typing import Optional diff --git a/gateway/channel_directory.py b/gateway/channel_directory.py index ecc54e644..0d1247217 100644 --- a/gateway/channel_directory.py +++ b/gateway/channel_directory.py @@ -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: diff --git a/gateway/platforms/base.py b/gateway/platforms/base.py index 66fc5bac2..e9464365c 100644 --- a/gateway/platforms/base.py +++ b/gateway/platforms/base.py @@ -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 diff --git a/gateway/platforms/feishu.py b/gateway/platforms/feishu.py index 9bbf2f62d..8ba41e8eb 100644 --- a/gateway/platforms/feishu.py +++ b/gateway/platforms/feishu.py @@ -60,7 +60,6 @@ try: CreateMessageRequestBody, GetChatRequest, GetMessageRequest, - GetImageRequest, GetMessageResourceRequest, P2ImMessageMessageReadV1, ReplyMessageRequest, diff --git a/gateway/platforms/matrix.py b/gateway/platforms/matrix.py index 2dc0c5a9b..2eb89d118 100644 --- a/gateway/platforms/matrix.py +++ b/gateway/platforms/matrix.py @@ -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( diff --git a/gateway/platforms/mattermost.py b/gateway/platforms/mattermost.py index 957026037..3835919a2 100644 --- a/gateway/platforms/mattermost.py +++ b/gateway/platforms/mattermost.py @@ -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 diff --git a/gateway/platforms/slack.py b/gateway/platforms/slack.py index 384f379d7..627829ca3 100644 --- a/gateway/platforms/slack.py +++ b/gateway/platforms/slack.py @@ -979,16 +979,7 @@ 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 key_parts = ["agent:main", "slack", "group", channel_id, thread_ts] diff --git a/gateway/platforms/telegram.py b/gateway/platforms/telegram.py index 355bf3aee..8c69c47b8 100644 --- a/gateway/platforms/telegram.py +++ b/gateway/platforms/telegram.py @@ -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), diff --git a/gateway/platforms/wecom.py b/gateway/platforms/wecom.py index d40b651c5..525a830b3 100644 --- a/gateway/platforms/wecom.py +++ b/gateway/platforms/wecom.py @@ -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 diff --git a/gateway/platforms/whatsapp.py b/gateway/platforms/whatsapp.py index ac94e4720..a6475dcb8 100644 --- a/gateway/platforms/whatsapp.py +++ b/gateway/platforms/whatsapp.py @@ -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__) diff --git a/gateway/run.py b/gateway/run.py index 56518be65..f6fb563ca 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -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): diff --git a/gateway/stream_consumer.py b/gateway/stream_consumer.py index 59e72755a..2cda33642 100644 --- a/gateway/stream_consumer.py +++ b/gateway/stream_consumer.py @@ -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 ) diff --git a/hermes_cli/auth.py b/hermes_cli/auth.py index 9e92b450d..1cdbadc79 100644 --- a/hermes_cli/auth.py +++ b/hermes_cli/auth.py @@ -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() diff --git a/hermes_cli/auth_commands.py b/hermes_cli/auth_commands.py index 395dbb76c..97c2d850b 100644 --- a/hermes_cli/auth_commands.py +++ b/hermes_cli/auth_commands.py @@ -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, diff --git a/hermes_cli/banner.py b/hermes_cli/banner.py index 7435750bc..b9701d547 100644 --- a/hermes_cli/banner.py +++ b/hermes_cli/banner.py @@ -5,7 +5,6 @@ Pure display functions with no HermesCLI state dependency. import json import logging -import os import shutil import subprocess import threading diff --git a/hermes_cli/callbacks.py b/hermes_cli/callbacks.py index 87f86b84d..ada413df7 100644 --- a/hermes_cli/callbacks.py +++ b/hermes_cli/callbacks.py @@ -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, diff --git a/hermes_cli/claw.py b/hermes_cli/claw.py index 87735f931..281ca37f5 100644 --- a/hermes_cli/claw.py +++ b/hermes_cli/claw.py @@ -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, ) diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 8863bda59..d90fc2155 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -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 diff --git a/hermes_cli/doctor.py b/hermes_cli/doctor.py index 08cf72334..876ab15d5 100644 --- a/hermes_cli/doctor.py +++ b/hermes_cli/doctor.py @@ -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 diff --git a/hermes_cli/logs.py b/hermes_cli/logs.py index 500cccd4f..d59849408 100644 --- a/hermes_cli/logs.py +++ b/hermes_cli/logs.py @@ -15,7 +15,6 @@ Usage examples:: hermes logs --since 30m -f # follow, starting 30 min ago """ -import os import re import sys import time diff --git a/hermes_cli/main.py b/hermes_cli/main.py index dae8cc958..3d1e28476 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -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')") diff --git a/hermes_cli/model_switch.py b/hermes_cli/model_switch.py index b2f763c61..988eeebdf 100644 --- a/hermes_cli/model_switch.py +++ b/hermes_cli/model_switch.py @@ -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 ( diff --git a/hermes_cli/plugins_cmd.py b/hermes_cli/plugins_cmd.py index bd6d7faba..5bfc488e3 100644 --- a/hermes_cli/plugins_cmd.py +++ b/hermes_cli/plugins_cmd.py @@ -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." diff --git a/hermes_cli/profiles.py b/hermes_cli/profiles.py index bb3f6b994..48ecbc4ca 100644 --- a/hermes_cli/profiles.py +++ b/hermes_cli/profiles.py @@ -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: diff --git a/hermes_cli/providers.py b/hermes_cli/providers.py index 890927884..0f2387065 100644 --- a/hermes_cli/providers.py +++ b/hermes_cli/providers.py @@ -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] = { diff --git a/hermes_cli/setup.py b/hermes_cli/setup.py index 29cb64721..d7786d1d2 100644 --- a/hermes_cli/setup.py +++ b/hermes_cli/setup.py @@ -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 ) diff --git a/hermes_cli/skin_engine.py b/hermes_cli/skin_engine.py index 62fac0eaf..16ec39cc9 100644 --- a/hermes_cli/skin_engine.py +++ b/hermes_cli/skin_engine.py @@ -96,7 +96,6 @@ Activate with ``/skin `` in the CLI or ``display.skin: `` in config. """ import logging -import os from dataclasses import dataclass, field from pathlib import Path from typing import Any, Dict, List, Optional, Tuple diff --git a/hermes_cli/uninstall.py b/hermes_cli/uninstall.py index 4a068b04b..7e35b8673 100644 --- a/hermes_cli/uninstall.py +++ b/hermes_cli/uninstall.py @@ -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 diff --git a/hermes_cli/webhook.py b/hermes_cli/webhook.py index 264e7f842..15f5ec43a 100644 --- a/hermes_cli/webhook.py +++ b/hermes_cli/webhook.py @@ -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 diff --git a/hermes_logging.py b/hermes_logging.py index 9a720bf68..6d8f4fa7b 100644 --- a/hermes_logging.py +++ b/hermes_logging.py @@ -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 diff --git a/hermes_state.py b/hermes_state.py index 6f6be056a..da632a9e1 100644 --- a/hermes_state.py +++ b/hermes_state.py @@ -16,7 +16,6 @@ Key design decisions: import json import logging -import os import random import re import sqlite3 diff --git a/hermes_time.py b/hermes_time.py index 4ec8dfe00..faf02bf87 100644 --- a/hermes_time.py +++ b/hermes_time.py @@ -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 "" diff --git a/mcp_serve.py b/mcp_serve.py index 93c439795..e8294d1f9 100644 --- a/mcp_serve.py +++ b/mcp_serve.py @@ -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") diff --git a/plugins/memory/byterover/__init__.py b/plugins/memory/byterover/__init__.py index ead87d0c2..d73440c78 100644 --- a/plugins/memory/byterover/__init__.py +++ b/plugins/memory/byterover/__init__.py @@ -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 diff --git a/plugins/memory/holographic/__init__.py b/plugins/memory/holographic/__init__.py index 3ffdda1d1..b1423c105 100644 --- a/plugins/memory/holographic/__init__.py +++ b/plugins/memory/holographic/__init__.py @@ -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 diff --git a/plugins/memory/holographic/store.py b/plugins/memory/holographic/store.py index ea15554a3..3dc66d686 100644 --- a/plugins/memory/holographic/store.py +++ b/plugins/memory/holographic/store.py @@ -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: diff --git a/plugins/memory/honcho/__init__.py b/plugins/memory/honcho/__init__.py index 336cf353d..db2773667 100644 --- a/plugins/memory/honcho/__init__.py +++ b/plugins/memory/honcho/__init__.py @@ -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 diff --git a/plugins/memory/honcho/cli.py b/plugins/memory/honcho/cli.py index 1735c0065..dff4b386a 100644 --- a/plugins/memory/honcho/cli.py +++ b/plugins/memory/honcho/cli.py @@ -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", diff --git a/plugins/memory/mem0/__init__.py b/plugins/memory/mem0/__init__.py index df0f56bcd..7e7d261fc 100644 --- a/plugins/memory/mem0/__init__.py +++ b/plugins/memory/mem0/__init__.py @@ -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 diff --git a/plugins/memory/retaindb/__init__.py b/plugins/memory/retaindb/__init__.py index 2a3b7a229..69b8a8cf5 100644 --- a/plugins/memory/retaindb/__init__.py +++ b/plugins/memory/retaindb/__init__.py @@ -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 diff --git a/run_agent.py b/run_agent.py index cc0e06bdc..4c6cf500e 100644 --- a/run_agent.py +++ b/run_agent.py @@ -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. diff --git a/scripts/release.py b/scripts/release.py index cfe360064..ea697cb3e 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -21,8 +21,6 @@ Usage: """ import argparse -import json -import os import re import shutil import subprocess diff --git a/scripts/sample_and_compress.py b/scripts/sample_and_compress.py index 419111d80..a6358f45b 100644 --- a/scripts/sample_and_compress.py +++ b/scripts/sample_and_compress.py @@ -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) diff --git a/skills/mlops/training/grpo-rl-training/templates/basic_grpo_training.py b/skills/mlops/training/grpo-rl-training/templates/basic_grpo_training.py index 228a93e7c..8ad45dfcf 100644 --- a/skills/mlops/training/grpo-rl-training/templates/basic_grpo_training.py +++ b/skills/mlops/training/grpo-rl-training/templates/basic_grpo_training.py @@ -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 diff --git a/skills/red-teaming/godmode/scripts/auto_jailbreak.py b/skills/red-teaming/godmode/scripts/auto_jailbreak.py index 754b405a8..0b17de509 100644 --- a/skills/red-teaming/godmode/scripts/auto_jailbreak.py +++ b/skills/red-teaming/godmode/scripts/auto_jailbreak.py @@ -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 diff --git a/skills/red-teaming/godmode/scripts/godmode_race.py b/skills/red-teaming/godmode/scripts/godmode_race.py index 60b916cba..ccd021392 100644 --- a/skills/red-teaming/godmode/scripts/godmode_race.py +++ b/skills/red-teaming/godmode/scripts/godmode_race.py @@ -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 = [ { diff --git a/skills/red-teaming/godmode/scripts/parseltongue.py b/skills/red-teaming/godmode/scripts/parseltongue.py index bf784d2ba..ba891c6ac 100644 --- a/skills/red-teaming/godmode/scripts/parseltongue.py +++ b/skills/red-teaming/godmode/scripts/parseltongue.py @@ -17,7 +17,6 @@ Usage: import re import base64 -import sys # ═══════════════════════════════════════════════════════════════════ # Trigger words that commonly trip safety classifiers diff --git a/tools/browser_camofox.py b/tools/browser_camofox.py index 91f8fa4fd..13e85c18d 100644 --- a/tools/browser_camofox.py +++ b/tools/browser_camofox.py @@ -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 diff --git a/tools/browser_tool.py b/tools/browser_tool.py index faa872a93..8ad3002bb 100644 --- a/tools/browser_tool.py +++ b/tools/browser_tool.py @@ -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() diff --git a/tools/code_execution_tool.py b/tools/code_execution_tool.py index 5c4658b6f..a7a806065 100644 --- a/tools/code_execution_tool.py +++ b/tools/code_execution_tool.py @@ -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) diff --git a/tools/cronjob_tools.py b/tools/cronjob_tools.py index 8dbcf7c3a..caedaca72 100644 --- a/tools/cronjob_tools.py +++ b/tools/cronjob_tools.py @@ -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() diff --git a/tools/debug_helpers.py b/tools/debug_helpers.py index 0bd5f2ac5..6f8acf229 100644 --- a/tools/debug_helpers.py +++ b/tools/debug_helpers.py @@ -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 diff --git a/tools/environments/daytona.py b/tools/environments/daytona.py index eb2a67311..e52459d8b 100644 --- a/tools/environments/daytona.py +++ b/tools/environments/daytona.py @@ -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 diff --git a/tools/environments/singularity.py b/tools/environments/singularity.py index 6643ea1b3..0ea5037c8 100644 --- a/tools/environments/singularity.py +++ b/tools/environments/singularity.py @@ -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 diff --git a/tools/mcp_oauth.py b/tools/mcp_oauth.py index 00172f340..c4d772676 100644 --- a/tools/mcp_oauth.py +++ b/tools/mcp_oauth.py @@ -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). diff --git a/tools/memory_tool.py b/tools/memory_tool.py index 91924f66b..f7b6bed2d 100644 --- a/tools/memory_tool.py +++ b/tools/memory_tool.py @@ -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: diff --git a/tools/rl_training_tool.py b/tools/rl_training_tool.py index 29919f222..7a6478b42 100644 --- a/tools/rl_training_tool.py +++ b/tools/rl_training_tool.py @@ -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), } diff --git a/tools/send_message_tool.py b/tools/send_message_tool.py index eff0e7b55..4e500e694 100644 --- a/tools/send_message_tool.py +++ b/tools/send_message_tool.py @@ -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: diff --git a/tools/skills_hub.py b/tools/skills_hub.py index 56c89ba71..d2d8127a8 100644 --- a/tools/skills_hub.py +++ b/tools/skills_hub.py @@ -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 diff --git a/tools/skills_tool.py b/tools/skills_tool.py index da023a143..c6b6cac3d 100644 --- a/tools/skills_tool.py +++ b/tools/skills_tool.py @@ -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__) diff --git a/tools/terminal_tool.py b/tools/terminal_tool.py index 305d08011..76946f80a 100644 --- a/tools/terminal_tool.py +++ b/tools/terminal_tool.py @@ -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() diff --git a/tools/todo_tool.py b/tools/todo_tool.py index b94e54742..d5dc33b50 100644 --- a/tools/todo_tool.py +++ b/tools/todo_tool.py @@ -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]: """ diff --git a/tools/tts_tool.py b/tools/tts_tool.py index a8c2ac05b..4cb6e64ee 100644 --- a/tools/tts_tool.py +++ b/tools/tts_tool.py @@ -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( diff --git a/tools/vision_tools.py b/tools/vision_tools.py index 404d06a50..8c9d6a9bc 100644 --- a/tools/vision_tools.py +++ b/tools/vision_tools.py @@ -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 diff --git a/tools/voice_mode.py b/tools/voice_mode.py index 53d9ecb00..1b09a178c 100644 --- a/tools/voice_mode.py +++ b/tools/voice_mode.py @@ -108,7 +108,7 @@ def detect_audio_environment() -> dict: ) return { - "available": len(warnings) == 0, + "available": not warnings, "warnings": warnings, "notices": notices, } diff --git a/tools/website_policy.py b/tools/website_policy.py index 93a2eb283..63fb75710 100644 --- a/tools/website_policy.py +++ b/tools/website_policy.py @@ -12,7 +12,6 @@ from __future__ import annotations import fnmatch import logging -import os import threading import time from pathlib import Path diff --git a/toolsets.py b/toolsets.py index 04e43b286..2a359b60a 100644 --- a/toolsets.py +++ b/toolsets.py @@ -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"]) } diff --git a/trajectory_compressor.py b/trajectory_compressor.py index 2dfdda7af..e4faf97a3 100644 --- a/trajectory_compressor.py +++ b/trajectory_compressor.py @@ -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: