refactor: remove dead code — 1,784 lines across 77 files (#9180)

Deep scan with vulture, pyflakes, and manual cross-referencing identified:
- 41 dead functions/methods (zero callers in production)
- 7 production-dead functions (only test callers, tests deleted)
- 5 dead constants/variables
- ~35 unused imports across agent/, hermes_cli/, tools/, gateway/

Categories of dead code removed:
- Refactoring leftovers: _set_default_model, _setup_copilot_reasoning_selection,
  rebuild_lookups, clear_session_context, get_logs_dir, clear_session
- Unused API surface: search_models_dev, get_pricing, skills_categories,
  get_read_files_summary, clear_read_tracker, menu_labels, get_spinner_list
- Dead compatibility wrappers: schedule_cronjob, list_cronjobs, remove_cronjob
- Stale debug helpers: get_debug_session_info copies in 4 tool files
  (centralized version in debug_helpers.py already exists)
- Dead gateway methods: send_emote, send_notice (matrix), send_reaction
  (bluebubbles), _normalize_inbound_text (feishu), fetch_room_history
  (matrix), _start_typing_indicator (signal), parse_feishu_post_content
- Dead constants: NOUS_API_BASE_URL, SKILLS_TOOL_DESCRIPTION,
  FILE_TOOLS, VALID_ASPECT_RATIOS, MEMORY_DIR
- Unused UI code: _interactive_provider_selection,
  _interactive_model_selection (superseded by prompt_toolkit picker)

Test suite verified: 609 tests covering affected files all pass.
Tests for removed functions deleted. Tests using removed utilities
(clear_read_tracker, MEMORY_DIR) updated to use internal APIs directly.
This commit is contained in:
Teknium
2026-04-13 16:32:04 -07:00
committed by GitHub
parent a66fc1365d
commit 8d023e43ed
77 changed files with 44 additions and 1784 deletions

View File

@@ -352,19 +352,6 @@ def load_permanent(patterns: set):
_permanent_approved.update(patterns)
def clear_session(session_key: str):
"""Clear all approvals and pending requests for a session."""
with _lock:
_session_approved.pop(session_key, None)
_session_yolo.discard(session_key)
_pending.pop(session_key, None)
_gateway_notify_cbs.pop(session_key, None)
# Signal ALL blocked threads so they don't hang forever
entries = _gateway_queues.pop(session_key, [])
for entry in entries:
entry.event.set()
# =========================================================================
# Config persistence for permanent allowlist

View File

@@ -382,42 +382,6 @@ def cronjob(
return tool_error(str(e), success=False)
# ---------------------------------------------------------------------------
# Compatibility wrappers
# ---------------------------------------------------------------------------
def schedule_cronjob(
prompt: str,
schedule: str,
name: Optional[str] = None,
repeat: Optional[int] = None,
deliver: Optional[str] = None,
model: Optional[str] = None,
provider: Optional[str] = None,
base_url: Optional[str] = None,
task_id: str = None,
) -> str:
return cronjob(
action="create",
prompt=prompt,
schedule=schedule,
name=name,
repeat=repeat,
deliver=deliver,
model=model,
provider=provider,
base_url=base_url,
task_id=task_id,
)
def list_cronjobs(include_disabled: bool = False, task_id: str = None) -> str:
return cronjob(action="list", include_disabled=include_disabled, task_id=task_id)
def remove_cronjob(job_id: str, task_id: str = None) -> str:
return cronjob(action="remove", job_id=job_id, task_id=task_id)
CRONJOB_SCHEMA = {
"name": "cronjob",

View File

@@ -20,9 +20,7 @@ Both ``code_execution_tool.py`` and ``tools/environments/local.py`` consult
from __future__ import annotations
import logging
import os
from contextvars import ContextVar
from pathlib import Path
from typing import Iterable
logger = logging.getLogger(__name__)

View File

@@ -449,38 +449,6 @@ def read_file_tool(path: str, offset: int = 1, limit: int = 500, task_id: str =
return tool_error(str(e))
def get_read_files_summary(task_id: str = "default") -> list:
"""Return a list of files read in this session for the given task.
Used by context compression to preserve file-read history across
compression boundaries.
"""
with _read_tracker_lock:
task_data = _read_tracker.get(task_id, {})
read_history = task_data.get("read_history", set())
seen_paths: dict = {}
for (path, offset, limit) in read_history:
if path not in seen_paths:
seen_paths[path] = []
seen_paths[path].append(f"lines {offset}-{offset + limit - 1}")
return [
{"path": p, "regions": regions}
for p, regions in sorted(seen_paths.items())
]
def clear_read_tracker(task_id: str = None):
"""Clear the read tracker.
Call with a task_id to clear just that task, or without to clear all.
Should be called when a session is destroyed to prevent memory leaks
in long-running gateway processes.
"""
with _read_tracker_lock:
if task_id:
_read_tracker.pop(task_id, None)
else:
_read_tracker.clear()
def reset_file_dedup(task_id: str = None):
@@ -719,12 +687,6 @@ def search_tool(pattern: str, target: str = "content", path: str = ".",
return tool_error(str(e))
FILE_TOOLS = [
{"name": "read_file", "function": read_file_tool},
{"name": "write_file", "function": write_file_tool},
{"name": "patch", "function": patch_tool},
{"name": "search_files", "function": search_tool}
]
# ---------------------------------------------------------------------------

View File

@@ -61,7 +61,6 @@ ASPECT_RATIO_MAP = {
"square": "square_hd",
"portrait": "portrait_16_9"
}
VALID_ASPECT_RATIOS = list(ASPECT_RATIO_MAP.keys())
# Configuration for automatic upscaling
UPSCALER_MODEL = "fal-ai/clarity-upscaler"
@@ -564,15 +563,6 @@ def check_image_generation_requirements() -> bool:
return False
def get_debug_session_info() -> Dict[str, Any]:
"""
Get information about the current debug session.
Returns:
Dict[str, Any]: Dictionary containing debug session information
"""
return _debug.get_session_info()
if __name__ == "__main__":
"""

View File

@@ -44,11 +44,6 @@ def get_memory_dir() -> Path:
"""Return the profile-scoped memories directory."""
return get_hermes_home() / "memories"
# Backward-compatible alias — gateway/run.py imports this at runtime inside
# a function body, so it gets the correct snapshot for that process. New code
# should prefer get_memory_dir().
MEMORY_DIR = get_memory_dir()
ENTRY_DELIMITER = "\n§\n"

View File

@@ -416,29 +416,6 @@ def check_moa_requirements() -> bool:
return check_openrouter_api_key()
def get_debug_session_info() -> Dict[str, Any]:
"""
Get information about the current debug session.
Returns:
Dict[str, Any]: Dictionary containing debug session information
"""
return _debug.get_session_info()
def get_available_models() -> Dict[str, List[str]]:
"""
Get information about available models for MoA processing.
Returns:
Dict[str, List[str]]: Dictionary with reference and aggregator models
"""
return {
"reference_models": REFERENCE_MODELS,
"aggregator_models": [AGGREGATOR_MODEL],
"supported_models": REFERENCE_MODELS + [AGGREGATOR_MODEL]
}
def get_moa_configuration() -> Dict[str, Any]:
"""

View File

@@ -872,55 +872,6 @@ def _unicode_char_name(char: str) -> str:
return names.get(char, f"U+{ord(char):04X}")
def _parse_llm_response(text: str, skill_name: str) -> List[Finding]:
"""Parse the LLM's JSON response into Finding objects."""
import json as json_mod
# Extract JSON from the response (handle markdown code blocks)
text = text.strip()
if text.startswith("```"):
lines = text.split("\n")
text = "\n".join(lines[1:-1] if lines[-1].startswith("```") else lines[1:])
try:
data = json_mod.loads(text)
except json_mod.JSONDecodeError:
return []
if not isinstance(data, dict):
return []
findings = []
for item in data.get("findings", []):
if not isinstance(item, dict):
continue
desc = item.get("description", "")
severity = item.get("severity", "medium")
if severity not in ("critical", "high", "medium", "low"):
severity = "medium"
if desc:
findings.append(Finding(
pattern_id="llm_audit",
severity=severity,
category="llm-detected",
file="(LLM analysis)",
line=0,
match=desc[:120],
description=f"LLM audit: {desc}",
))
return findings
def _get_configured_model() -> str:
"""Load the user's configured model from ~/.hermes/config.yaml."""
try:
from hermes_cli.config import load_config
config = load_config()
return config.get("model", "")
except Exception:
return ""
# ---------------------------------------------------------------------------
# Internal helpers

View File

@@ -447,10 +447,6 @@ def _get_category_from_path(skill_path: Path) -> Optional[str]:
return None
# Token estimation — use the shared implementation from model_metadata.
from agent.model_metadata import estimate_tokens_rough as _estimate_tokens
def _parse_tags(tags_value) -> List[str]:
"""
Parse tags from frontmatter value.
@@ -629,85 +625,6 @@ def _load_category_description(category_dir: Path) -> Optional[str]:
return None
def skills_categories(verbose: bool = False, task_id: str = None) -> str:
"""
List available skill categories with descriptions (progressive disclosure tier 0).
Returns category names and descriptions for efficient discovery before drilling down.
Categories can have a DESCRIPTION.md file with a description frontmatter field
or first paragraph to explain what skills are in that category.
Args:
verbose: If True, include skill counts per category (default: False, but currently always included)
task_id: Optional task identifier used to probe the active backend
Returns:
JSON string with list of categories and their descriptions
"""
try:
# Use module-level SKILLS_DIR (respects monkeypatching) + external dirs
all_dirs = [SKILLS_DIR] if SKILLS_DIR.exists() else []
try:
from agent.skill_utils import get_external_skills_dirs
all_dirs.extend(d for d in get_external_skills_dirs() if d.exists())
except Exception:
pass
if not all_dirs:
return json.dumps(
{
"success": True,
"categories": [],
"message": "No skills directory found.",
},
ensure_ascii=False,
)
category_dirs = {}
category_counts: Dict[str, int] = {}
for scan_dir in all_dirs:
for skill_md in scan_dir.rglob("SKILL.md"):
if any(part in _EXCLUDED_SKILL_DIRS for part in skill_md.parts):
continue
try:
frontmatter, _ = _parse_frontmatter(
skill_md.read_text(encoding="utf-8")[:4000]
)
except Exception:
frontmatter = {}
if not skill_matches_platform(frontmatter):
continue
category = _get_category_from_path(skill_md)
if category:
category_counts[category] = category_counts.get(category, 0) + 1
if category not in category_dirs:
category_dirs[category] = skill_md.parent.parent
categories = []
for name in sorted(category_dirs.keys()):
category_dir = category_dirs[name]
description = _load_category_description(category_dir)
cat_entry = {"name": name, "skill_count": category_counts[name]}
if description:
cat_entry["description"] = description
categories.append(cat_entry)
return json.dumps(
{
"success": True,
"categories": categories,
"hint": "If a category is relevant to your task, use skills_list with that category to see available skills",
},
ensure_ascii=False,
)
except Exception as e:
return tool_error(str(e), success=False)
def skills_list(category: str = None, task_id: str = None) -> str:
"""
List all available skills (progressive disclosure tier 1 - minimal metadata).
@@ -1240,19 +1157,6 @@ def skill_view(name: str, file_path: str = None, task_id: str = None) -> str:
return tool_error(str(e), success=False)
# Tool description for model_tools.py
SKILLS_TOOL_DESCRIPTION = """Access skill documents providing specialized instructions, guidelines, and executable knowledge.
Progressive disclosure workflow:
1. skills_list() - Returns metadata (name, description, tags, linked_file_count) for all skills
2. skill_view(name) - Loads full SKILL.md content + shows available linked_files
3. skill_view(name, file_path) - Loads specific linked file (e.g., 'references/api.md', 'scripts/train.py')
Skills may include:
- references/: Additional documentation, API specs, examples
- templates/: Output formats, config files, boilerplate code
- assets/: Supplementary files (agentskills.io standard)
- scripts/: Executable helpers (Python, shell scripts)"""
if __name__ == "__main__":

View File

@@ -56,9 +56,6 @@ from tools.interrupt import is_interrupted, _interrupt_event # noqa: F401 — r
# display_hermes_home imported lazily at call site (stale-module safety during hermes update)
def ensure_minisweagent_on_path(_repo_root: Path | None = None) -> None:
"""Backward-compatible no-op after minisweagent_path.py removal."""
return
# =============================================================================
@@ -140,7 +137,6 @@ def set_approval_callback(cb):
# Dangerous command detection + approval now consolidated in tools/approval.py
from tools.approval import (
check_dangerous_command as _check_dangerous_command_impl,
check_all_command_guards as _check_all_guards_impl,
)
@@ -937,29 +933,6 @@ def is_persistent_env(task_id: str) -> bool:
return bool(getattr(env, "_persistent", False))
def get_active_environments_info() -> Dict[str, Any]:
"""Get information about currently active environments."""
info = {
"count": len(_active_environments),
"task_ids": list(_active_environments.keys()),
"workdirs": {},
}
# Calculate total disk usage (per-task to avoid double-counting)
total_size = 0
for task_id in _active_environments:
scratch_dir = _get_scratch_dir()
pattern = f"hermes-*{task_id[:8]}*"
import glob
for path in glob.glob(str(scratch_dir / pattern)):
try:
size = sum(f.stat().st_size for f in Path(path).rglob('*') if f.is_file())
total_size += size
except OSError as e:
logger.debug("Could not stat path %s: %s", path, e)
info["total_disk_usage_mb"] = round(total_size / (1024 * 1024), 2)
return info
def cleanup_all_environments():

View File

@@ -37,8 +37,6 @@ from utils import is_truthy_value
from tools.managed_tool_gateway import resolve_managed_tool_gateway
from tools.tool_backend_helpers import managed_nous_tools_enabled, resolve_openai_audio_api_key
from hermes_constants import get_hermes_home
logger = logging.getLogger(__name__)
# ---------------------------------------------------------------------------
@@ -93,35 +91,6 @@ _local_model_name: Optional[str] = None
# ---------------------------------------------------------------------------
def get_stt_model_from_config() -> Optional[str]:
"""Read the STT model name from ~/.hermes/config.yaml.
Provider-aware: reads from the correct provider-specific section
(``stt.local.model``, ``stt.openai.model``, etc.). Falls back to
the legacy flat ``stt.model`` key only for cloud providers — if the
resolved provider is ``local`` the legacy key is ignored to prevent
OpenAI model names (e.g. ``whisper-1``) from being fed to
faster-whisper.
Silently returns ``None`` on any error (missing file, bad YAML, etc.).
"""
try:
stt_cfg = _load_stt_config()
provider = stt_cfg.get("provider", DEFAULT_PROVIDER)
# Read from the provider-specific section first
provider_model = stt_cfg.get(provider, {}).get("model")
if provider_model:
return provider_model
# Legacy flat key — only honour for non-local providers to avoid
# feeding OpenAI model names (whisper-1) to faster-whisper.
if provider not in ("local", "local_command"):
legacy = stt_cfg.get("model")
if legacy:
return legacy
except Exception:
pass
return None
def _load_stt_config() -> dict:
"""Load the ``stt`` section from user config, falling back to defaults."""

View File

@@ -689,15 +689,6 @@ def check_vision_requirements() -> bool:
return False
def get_debug_session_info() -> Dict[str, Any]:
"""
Get information about the current debug session.
Returns:
Dict[str, Any]: Dictionary containing debug session information
"""
return _debug.get_session_info()
if __name__ == "__main__":
"""

View File

@@ -63,11 +63,6 @@ def _termux_microphone_command() -> Optional[str]:
return shutil.which("termux-microphone-record")
def _termux_media_player_command() -> Optional[str]:
if not _is_termux_environment():
return None
return shutil.which("termux-media-player")
def _termux_api_app_installed() -> bool:
if not _is_termux_environment():

View File

@@ -1932,9 +1932,6 @@ def check_auxiliary_model() -> bool:
return client is not None
def get_debug_session_info() -> Dict[str, Any]:
"""Get information about the current debug session."""
return _debug.get_session_info()
if __name__ == "__main__":