chore: remove ~100 unused imports across 55 files (#3016)

Automated cleanup via pyflakes + autoflake with manual review.

Changes:
- Removed unused stdlib imports (os, sys, json, pathlib.Path, etc.)
- Removed unused typing imports (List, Dict, Any, Optional, Tuple, Set, etc.)
- Removed unused internal imports (hermes_cli.auth, hermes_cli.config, etc.)
- Fixed cli.py: removed 8 shadowed banner imports (imported from hermes_cli.banner
  then immediately redefined locally — only build_welcome_banner is actually used)
- Added noqa comments to imports that appear unused but serve a purpose:
  - Re-exports (gateway/session.py SessionResetPolicy, tools/terminal_tool.py
    is_interrupted/_interrupt_event)
  - SDK presence checks in try/except (daytona, fal_client, discord)
  - Test mock targets (auxiliary_client.py Path, mcp_config.py get_hermes_home)

Zero behavioral changes. Full test suite passes (6162/6162, 2 pre-existing
streaming test failures unrelated to this change).
This commit is contained in:
Teknium 2026-03-25 15:02:03 -07:00 committed by GitHub
parent 861624d4e9
commit 8bb1d15da4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
55 changed files with 51 additions and 119 deletions

View file

@ -10,7 +10,7 @@ thread while the event loop lives on the main thread).
import asyncio
import json
import logging
from collections import defaultdict, deque
from collections import deque
from typing import Any, Callable, Deque, Dict
import acp

View file

@ -5,14 +5,11 @@ from __future__ import annotations
import asyncio
import logging
from concurrent.futures import TimeoutError as FutureTimeout
from typing import Any, Callable, Optional
from typing import Callable
from acp.schema import (
AllowedOutcome,
DeniedOutcome,
PermissionOption,
RequestPermissionRequest,
SelectedPermissionOutcome,
)
logger = logging.getLogger(__name__)

View file

@ -41,7 +41,7 @@ import logging
import os
import threading
import time
from pathlib import Path
from pathlib import Path # noqa: F401 — used by test mocks
from types import SimpleNamespace
from typing import Any, Dict, List, Optional, Tuple

View file

@ -14,7 +14,6 @@ Improvements over v1:
"""
import logging
import os
from typing import Any, Dict, List, Optional
from agent.auxiliary_client import call_llm

20
cli.py
View file

@ -458,13 +458,8 @@ from run_agent import AIAgent
from model_tools import get_tool_definitions, get_toolset_for_tool
# Extracted CLI modules (Phase 3)
from hermes_cli.banner import (
cprint as _cprint, _GOLD, _BOLD, _DIM, _RST,
HERMES_AGENT_LOGO, HERMES_CADUCEUS, COMPACT_BANNER,
build_welcome_banner,
)
from hermes_cli.commands import COMMANDS, SlashCommandCompleter, SlashCommandAutoSuggest
from hermes_cli import callbacks as _callbacks
from hermes_cli.banner import build_welcome_banner
from hermes_cli.commands import SlashCommandCompleter, SlashCommandAutoSuggest
from toolsets import get_all_toolsets, get_toolset_info, validate_toolset
# Cron job system for scheduled tasks (execution is handled by the gateway)
@ -2341,7 +2336,7 @@ class HermesCLI:
/rollback diff <N> preview changes since checkpoint N
/rollback <N> <file> restore a single file from checkpoint N
"""
from tools.checkpoint_manager import CheckpointManager, format_checkpoint_list
from tools.checkpoint_manager import format_checkpoint_list
if not hasattr(self, 'agent') or not self.agent:
print(" No active agent session.")
@ -2541,7 +2536,7 @@ class HermesCLI:
def _show_tool_availability_warnings(self):
"""Show warnings about disabled tools due to missing API keys."""
try:
from model_tools import check_tool_availability, TOOLSET_REQUIREMENTS
from model_tools import check_tool_availability
available, unavailable = check_tool_availability()
@ -4134,7 +4129,6 @@ class HermesCLI:
def _handle_browser_command(self, cmd: str):
"""Handle /browser connect|disconnect|status — manage live Chrome CDP connection."""
import platform as _plat
import subprocess as _sp
parts = cmd.strip().split(None, 1)
sub = parts[1].lower().strip() if len(parts) > 1 else "status"
@ -4636,7 +4630,7 @@ class HermesCLI:
sees the updated tools on the next turn.
"""
try:
from tools.mcp_tool import shutdown_mcp_servers, discover_mcp_tools, _load_mcp_config, _servers, _lock
from tools.mcp_tool import shutdown_mcp_servers, discover_mcp_tools, _servers, _lock
# Capture old server names
with _lock:
@ -4956,7 +4950,6 @@ class HermesCLI:
try:
from tools.tts_tool import text_to_speech_tool
from tools.voice_mode import play_audio_file
import json
import re
# Strip markdown and non-speech content for cleaner TTS
@ -6530,8 +6523,7 @@ class HermesCLI:
"""Return provider/model info for /model autocomplete."""
try:
from hermes_cli.models import (
_PROVIDER_LABELS, _PROVIDER_MODELS, normalize_provider,
provider_model_ids,
_PROVIDER_LABELS, normalize_provider, provider_model_ids,
)
current = getattr(cli_ref, "provider", None) or getattr(cli_ref, "requested_provider", "openrouter")
current = normalize_provider(current)

View file

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

View file

@ -9,7 +9,6 @@ action="list" and for resolving human-friendly channel names to numeric IDs.
import json
import logging
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Optional
from hermes_cli.config import get_hermes_home
@ -90,7 +89,7 @@ def _build_discord(adapter) -> List[Dict[str, str]]:
return channels
try:
import discord as _discord
import discord as _discord # noqa: F401 — SDK presence check
except ImportError:
return channels
@ -119,7 +118,6 @@ def _build_slack(adapter) -> List[Dict[str, str]]:
return _build_from_sessions("slack")
try:
import asyncio
from tools.send_message_tool import _send_slack # noqa: F401
# Use the Slack Web API directly if available
except Exception:

View file

@ -13,7 +13,6 @@ from pathlib import Path
from datetime import datetime
from dataclasses import dataclass
from typing import Dict, List, Optional, Any, Union
from enum import Enum
from hermes_cli.config import get_hermes_home

View file

@ -21,8 +21,6 @@ Errors in hooks are caught and logged but never block the main pipeline.
import asyncio
import importlib.util
import os
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional
import yaml

View file

@ -12,7 +12,6 @@ the full SessionStore machinery.
import json
import logging
from datetime import datetime
from pathlib import Path
from typing import Optional
from hermes_cli.config import get_hermes_home

View file

@ -20,7 +20,7 @@ import threading
import time
from collections import defaultdict
from pathlib import Path
from typing import Callable, Dict, List, Optional, Any
from typing import Callable, Dict, Optional, Any
logger = logging.getLogger(__name__)

View file

@ -24,7 +24,6 @@ import re
import smtplib
import ssl
import uuid
from datetime import datetime
from email.header import decode_header
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
@ -454,7 +453,6 @@ class EmailAdapter(BasePlatformAdapter):
async def send_typing(self, chat_id: str, metadata: Optional[Dict[str, Any]] = None) -> None:
"""Email has no typing indicator — no-op."""
pass
async def send_image(
self,

View file

@ -19,7 +19,7 @@ import os
import time
import uuid
from datetime import datetime
from typing import Any, Dict, List, Optional, Set
from typing import Any, Dict, Optional, Set
try:
import aiohttp
@ -435,7 +435,6 @@ class HomeAssistantAdapter(BasePlatformAdapter):
async def send_typing(self, chat_id: str, metadata=None) -> None:
"""No typing indicator for Home Assistant."""
pass
async def get_chat_info(self, chat_id: str) -> Dict[str, Any]:
"""Return basic info about the HA event channel."""

View file

@ -17,14 +17,13 @@ Environment variables:
from __future__ import annotations
import asyncio
import json
import logging
import mimetypes
import os
import re
import time
from pathlib import Path
from typing import Any, Dict, List, Optional, Set
from typing import Any, Dict, Optional, Set
from gateway.config import Platform, PlatformConfig
from gateway.platforms.base import (

View file

@ -20,7 +20,7 @@ import os
import re
import time
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple
from typing import Any, Dict, List, Optional
from gateway.config import Platform, PlatformConfig
from gateway.platforms.base import (

View file

@ -12,7 +12,7 @@ import asyncio
import logging
import os
import re
from typing import Dict, List, Optional, Any
from typing import Dict, Optional, Any
try:
from slack_bolt.async_app import AsyncApp
@ -37,8 +37,6 @@ from gateway.platforms.base import (
SendResult,
SUPPORTED_DOCUMENT_TYPES,
cache_document_from_bytes,
cache_image_from_url,
cache_audio_from_url,
)

View file

@ -17,12 +17,11 @@ Gateway-specific env vars:
import asyncio
import base64
import json
import logging
import os
import re
import urllib.parse
from typing import Any, Dict, List, Optional
from typing import Any, Dict, Optional
from gateway.config import Platform, PlatformConfig
from gateway.platforms.base import (

View file

@ -11,7 +11,7 @@ import asyncio
import logging
import os
import re
from typing import Dict, List, Optional, Any
from typing import Dict, Optional, Any
logger = logging.getLogger(__name__)

View file

@ -16,7 +16,6 @@ with different backends via a bridge pattern.
"""
import asyncio
import json
import logging
import os
import platform
@ -24,7 +23,7 @@ import subprocess
_IS_WINDOWS = platform.system() == "Windows"
from pathlib import Path
from typing import Dict, List, Optional, Any
from typing import Dict, Optional, Any
from hermes_cli.config import get_hermes_home

View file

@ -220,7 +220,7 @@ from gateway.session import (
build_session_context_prompt,
build_session_key,
)
from gateway.delivery import DeliveryRouter, DeliveryTarget
from gateway.delivery import DeliveryRouter
from gateway.platforms.base import BasePlatformAdapter, MessageEvent, MessageType
logger = logging.getLogger(__name__)
@ -2771,8 +2771,6 @@ class GatewayRunner:
"""Handle /model command - show or change the current model."""
import yaml
from hermes_cli.models import (
parse_model_input,
validate_requested_model,
curated_models_for_provider,
normalize_provider,
_PROVIDER_LABELS,

View file

@ -16,7 +16,7 @@ import re
import uuid
from pathlib import Path
from datetime import datetime, timedelta
from dataclasses import dataclass, field
from dataclasses import dataclass
from typing import Dict, List, Optional, Any
logger = logging.getLogger(__name__)
@ -59,7 +59,7 @@ def _looks_like_phone(value: str) -> bool:
from .config import (
Platform,
GatewayConfig,
SessionResetPolicy,
SessionResetPolicy, # noqa: F401 — re-exported via gateway/__init__.py
HomeChannel,
)

View file

@ -9,9 +9,7 @@ Cache location: ~/.hermes/sticker_cache.json
"""
import json
import os
import time
from pathlib import Path
from typing import Optional
from hermes_cli.config import get_hermes_home

View file

@ -11,7 +11,7 @@ import subprocess
import threading
import time
from pathlib import Path
from typing import Dict, List, Any, Optional
from typing import Dict, List, Optional
from rich.console import Console
from rich.panel import Panel
@ -257,7 +257,7 @@ def build_welcome_banner(console: Console, model: str, cwd: str,
get_toolset_for_tool: Callable to map tool name -> toolset name.
context_length: Model's context window size in tokens.
"""
from model_tools import check_tool_availability, TOOLSET_REQUIREMENTS
from model_tools import check_tool_availability
if get_toolset_for_tool is None:
from model_tools import get_toolset_for_tool

View file

@ -18,10 +18,8 @@ from hermes_cli.setup import (
print_header,
print_info,
print_success,
print_warning,
print_error,
prompt_yes_no,
prompt_choice,
)
logger = logging.getLogger(__name__)

View file

@ -13,8 +13,7 @@ from __future__ import annotations
import os
import re
from collections.abc import Callable, Mapping
from dataclasses import dataclass, field
from pathlib import Path
from dataclasses import dataclass
from typing import Any
from prompt_toolkit.auto_suggest import AutoSuggest, Suggestion

View file

@ -21,12 +21,11 @@ from __future__ import annotations
import json
import logging
import os
import re
import shutil
import subprocess
import time
from pathlib import Path
from typing import Any, Optional
from typing import Optional
logger = logging.getLogger(__name__)

View file

@ -8,7 +8,6 @@ import os
import sys
import subprocess
import shutil
from pathlib import Path
from hermes_cli.config import get_project_root, get_hermes_home, get_env_path
@ -448,7 +447,7 @@ def run_doctor(args):
check_fail("DAYTONA_API_KEY not set", "(required for TERMINAL_ENV=daytona)")
issues.append("Set DAYTONA_API_KEY environment variable")
try:
from daytona import Daytona
from daytona import Daytona # noqa: F401 — SDK presence check
check_ok("daytona SDK", "(installed)")
except ImportError:
check_fail("daytona SDK not installed", "(pip install daytona)")

View file

@ -4,7 +4,6 @@ from __future__ import annotations
import os
from pathlib import Path
from typing import Iterable
from dotenv import load_dotenv

View file

@ -548,7 +548,6 @@ def cmd_gateway(args):
def cmd_whatsapp(args):
"""Set up WhatsApp: choose mode, configure, install bridge, pair via QR."""
import os
import subprocess
from pathlib import Path
from hermes_cli.config import get_env_value, save_env_value
@ -742,12 +741,9 @@ def cmd_setup(args):
def cmd_model(args):
"""Select default model — starts with provider selection, then model picker."""
from hermes_cli.auth import (
resolve_provider, get_provider_auth_state, PROVIDER_REGISTRY,
_prompt_model_selection, _save_model_choice, _update_config_for_provider,
resolve_nous_runtime_credentials, fetch_nous_models, AuthError, format_auth_error,
_login_nous,
resolve_provider, AuthError, format_auth_error,
)
from hermes_cli.config import load_config, save_config, get_env_value, save_env_value
from hermes_cli.config import load_config, get_env_value
config = load_config()
current_model = config.get("model")
@ -1983,7 +1979,7 @@ def _model_flow_api_key_provider(config, provider_id, current_model=""):
"""Generic flow for API-key providers (z.ai, MiniMax)."""
from hermes_cli.auth import (
PROVIDER_REGISTRY, _prompt_model_selection, _save_model_choice,
_update_config_for_provider, deactivate_provider,
deactivate_provider,
)
from hermes_cli.config import get_env_value, save_env_value, load_config, save_config
@ -2167,7 +2163,7 @@ def _model_flow_anthropic(config, current_model=""):
import os
from hermes_cli.auth import (
PROVIDER_REGISTRY, _prompt_model_selection, _save_model_choice,
_update_config_for_provider, deactivate_provider,
deactivate_provider,
)
from hermes_cli.config import (
get_env_value, save_env_value, load_config, save_config,

View file

@ -14,15 +14,14 @@ import logging
import os
import re
import time
from pathlib import Path
from typing import Any, Dict, List, Optional, Set, Tuple
from typing import Any, Dict, List, Optional, Tuple
from hermes_cli.config import (
load_config,
save_config,
get_env_value,
save_env_value,
get_hermes_home,
get_hermes_home, # noqa: F401 — used by test mocks
)
from hermes_cli.colors import Colors, color

View file

@ -13,9 +13,7 @@ concerns: state mutation, config persistence, output formatting.
from __future__ import annotations
import os
from dataclasses import dataclass, field
from typing import Optional
from dataclasses import dataclass
@dataclass

View file

@ -283,7 +283,6 @@ from hermes_cli.config import (
save_env_value,
get_env_value,
ensure_hermes_home,
DEFAULT_CONFIG,
)
from hermes_cli.colors import Colors, color
@ -798,15 +797,11 @@ def setup_model_provider(config: dict):
"""Configure the inference provider and default model."""
from hermes_cli.auth import (
get_active_provider,
get_provider_auth_state,
PROVIDER_REGISTRY,
format_auth_error,
AuthError,
fetch_nous_models,
resolve_nous_runtime_credentials,
_update_config_for_provider,
_login_openai_codex,
get_codex_auth_status,
resolve_codex_runtime_credentials,
DEFAULT_CODEX_BASE_URL,
detect_external_credentials,
@ -975,7 +970,7 @@ def setup_model_provider(config: dict):
print()
try:
from hermes_cli.auth import _login_nous, ProviderConfig
from hermes_cli.auth import _login_nous
import argparse
mock_args = argparse.Namespace(
@ -3303,7 +3298,6 @@ def _run_quick_setup(config: dict, hermes_home):
get_missing_env_vars,
get_missing_config_fields,
check_config_version,
migrate_config,
)
print()

View file

@ -11,7 +11,7 @@ Config stored in ~/.hermes/config.yaml under:
telegram: [skill-c]
cli: []
"""
from typing import Dict, List, Optional, Set
from typing import List, Optional, Set
from hermes_cli.config import load_config, save_config
from hermes_cli.colors import Colors, color

View file

@ -186,7 +186,7 @@ def do_browse(page: int = 1, page_size: int = 20, source: str = "all",
Official skills are always shown first, regardless of source filter.
"""
from tools.skills_hub import (
GitHubAuth, create_source_router, OptionalSkillSource, SkillMeta,
GitHubAuth, create_source_router,
)
# Clamp page_size to safe range

View file

@ -13,11 +13,9 @@ import sys
from pathlib import Path
from typing import Dict, List, Optional, Set
import os
from hermes_cli.config import (
load_config, save_config, get_env_value, save_env_value,
get_hermes_home,
)
from hermes_cli.colors import Colors, color
@ -382,7 +380,7 @@ def _platform_toolset_summary(config: dict, platforms: Optional[List[str]] = Non
def _get_platform_tools(config: dict, platform: str) -> Set[str]:
"""Resolve which individual toolset names are enabled for a platform."""
from toolsets import resolve_toolset, TOOLSETS
from toolsets import resolve_toolset
platform_toolsets = config.get("platform_toolsets", {})
toolset_names = platform_toolsets.get(platform)

View file

@ -7,11 +7,9 @@ Provides options for:
"""
import os
import sys
import shutil
import subprocess
from pathlib import Path
from typing import Optional
from hermes_cli.colors import Colors, color

View file

@ -15,7 +15,7 @@ crashes due to a bad timezone string.
import logging
import os
from datetime import datetime, timezone as _tz
from datetime import datetime
from pathlib import Path
from typing import Optional

View file

@ -53,8 +53,7 @@ else:
# Import agent and tools
from run_agent import AIAgent
from model_tools import get_tool_definitions, check_toolset_requirements
from tools.rl_training_tool import check_rl_api_keys, get_missing_keys
from tools.rl_training_tool import get_missing_keys
# ============================================================================

View file

@ -65,7 +65,6 @@ from tools.terminal_tool import cleanup_vm
from tools.interrupt import set_interrupt as _set_interrupt
from tools.browser_tool import cleanup_browser
import requests
from hermes_constants import OPENROUTER_BASE_URL

View file

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

View file

@ -12,7 +12,7 @@ a thin dispatcher that delegates to a platform-provided callback.
"""
import json
from typing import Dict, Any, List, Optional, Callable
from typing import List, Optional, Callable
# Maximum number of predefined choices the agent can offer.

View file

@ -171,7 +171,6 @@ def _build_child_agent(
model on OpenRouter while the parent runs on Nous Portal).
"""
from run_agent import AIAgent
import model_tools
# When no explicit toolsets given, inherit from parent's enabled toolsets
# so disabled tools (e.g. web) don't leak to subagents.

View file

@ -27,11 +27,10 @@ Usage:
import os
import re
import json
import difflib
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Optional, List, Dict, Any, Tuple
from typing import Optional, List, Dict, Any
from pathlib import Path

View file

@ -4,9 +4,7 @@
import errno
import json
import logging
import os
import threading
from typing import Optional
from tools.file_operations import ShellFileOperations
from agent.redact import redact_sensitive_text
@ -50,8 +48,8 @@ def _get_file_ops(task_id: str = "default") -> ShellFileOperations:
from tools.terminal_tool import (
_active_environments, _env_lock, _create_environment,
_get_env_config, _last_activity, _start_cleanup_thread,
_check_disk_usage_warning,
_creation_locks, _creation_locks_lock,
_creation_locks,
_creation_locks_lock,
)
import time

View file

@ -416,7 +416,7 @@ def check_image_generation_requirements() -> bool:
return False
# Check if fal_client is available
import fal_client
import fal_client # noqa: F401 — SDK presence check
return True
except ImportError:

View file

@ -34,7 +34,6 @@ import logging
import os
import platform
import shlex
import shutil
import signal
import subprocess
import threading
@ -44,7 +43,6 @@ import uuid
_IS_WINDOWS = platform.system() == "Windows"
from tools.environments.local import _find_shell, _sanitize_subprocess_env
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional
from hermes_cli.config import get_hermes_home

View file

@ -16,7 +16,7 @@ Import chain (circular-import safe):
import json
import logging
from typing import Any, Callable, Dict, List, Optional, Set
from typing import Callable, Dict, List, Optional, Set
logger = logging.getLogger(__name__)

View file

@ -40,7 +40,7 @@ import uuid
import logging
from datetime import datetime
import yaml
from dataclasses import dataclass, field
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, List, Optional

View file

@ -379,7 +379,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, _escape_mdv2, _strip_mdv2
from gateway.platforms.telegram import TelegramAdapter, _strip_mdv2
_adapter = TelegramAdapter.__new__(TelegramAdapter)
formatted = _adapter.format_message(message)
except Exception:

View file

@ -18,7 +18,6 @@ Flow:
import asyncio
import concurrent.futures
import json
import os
import logging
from typing import Dict, Any, List, Optional, Union

View file

@ -32,7 +32,7 @@ import httpx
import yaml
from tools.skills_guard import (
ScanResult, scan_skill, should_allow_install, content_hash, TRUSTED_REPOS,
ScanResult, content_hash, TRUSTED_REPOS,
)
logger = logging.getLogger(__name__)

View file

@ -31,7 +31,6 @@ import json
import logging
import os
import platform
import sys
import time
import threading
import atexit
@ -48,7 +47,7 @@ logger = logging.getLogger(__name__)
# The terminal tool polls this during command execution so it can kill
# long-running subprocesses immediately instead of blocking until timeout.
# ---------------------------------------------------------------------------
from tools.interrupt import is_interrupted, _interrupt_event
from tools.interrupt import is_interrupted, _interrupt_event # noqa: F401 — re-exported
# =============================================================================
@ -1232,7 +1231,7 @@ def check_terminal_requirements() -> bool:
return True
elif env_type == "daytona":
from daytona import Daytona
from daytona import Daytona # noqa: F401 — SDK presence check
return os.getenv("DAYTONA_API_KEY") is not None
else:

View file

@ -629,7 +629,6 @@ def stream_tts_to_speaker(
if client is not None:
try:
sd = _import_sounddevice()
import numpy as _np
output_stream = sd.OutputStream(
samplerate=24000, channels=1, dtype="int16",
)

View file

@ -28,7 +28,6 @@ Usage:
)
"""
import asyncio
import base64
import json
import logging

View file

@ -19,7 +19,6 @@ import tempfile
import threading
import time
import wave
from pathlib import Path
from typing import Any, Dict, List, Optional
logger = logging.getLogger(__name__)
@ -710,7 +709,7 @@ def check_voice_requirements() -> Dict[str, Any]:
``missing_packages``, and ``details``.
"""
# Determine STT provider availability
from tools.transcription_tools import _get_provider, _load_stt_config, is_stt_enabled, _HAS_FASTER_WHISPER
from tools.transcription_tools import _get_provider, _load_stt_config, is_stt_enabled
stt_config = _load_stt_config()
stt_enabled = is_stt_enabled(stt_config)
stt_provider = _get_provider(stt_config)