diff --git a/.claude/skills/adding-mcp-hosts/references/testing-fixtures.md b/.claude/skills/adding-mcp-hosts/references/testing-fixtures.md index b83e5a3..3785cfe 100644 --- a/.claude/skills/adding-mcp-hosts/references/testing-fixtures.md +++ b/.claude/skills/adding-mcp-hosts/references/testing-fixtures.md @@ -27,7 +27,33 @@ Minimal example (modeled on the `lmstudio` entry, which uses `CLAUDE_FIELDS`): For hosts with extra fields, add them alongside the universals (see `gemini` or `codex` entries for examples with `httpUrl`, `timeout`, `includeTools`, `cwd`, etc.). -## 2. host_registry.py entries +## 2. test_adapter_protocol.py entries + +`tests/unit/mcp/test_adapter_protocol.py` has two **static** lists that are NOT auto-updated by the data-driven infrastructure. Both must be updated manually: + +**`ALL_ADAPTERS`** -- append the new adapter class: + +```python +ALL_ADAPTERS = [ + # ... existing entries ... + NewHostAdapter, +] +``` + +**`HOST_ADAPTER_MAP`** -- add the `MCPHostType → adapter class` mapping: + +```python +HOST_ADAPTER_MAP = { + # ... existing entries ... + MCPHostType.NEW_HOST: NewHostAdapter, +} +``` + +Import `NewHostAdapter` and `MCPHostType.NEW_HOST` at the top of the file alongside the existing imports. Missing either entry means the AP-01…AP-06 protocol compliance tests silently skip the new adapter — they pass without covering it. + +--- + +## 3. host_registry.py entries Make three additions in `tests/test_data/mcp_adapters/host_registry.py`. diff --git a/docs/articles/users/CLIReference.md b/docs/articles/users/CLIReference.md index 4eb6951..7c9c8d3 100644 --- a/docs/articles/users/CLIReference.md +++ b/docs/articles/users/CLIReference.md @@ -50,6 +50,7 @@ These flags are accepted by the top-level parser and apply to all commands unles | `--envs-dir` | path | Directory to store environments | `~/.hatch/envs` | | `--cache-ttl` | int | Cache time-to-live in seconds | `86400` (1 day) | | `--cache-dir` | path | Directory to store cached packages | `~/.hatch/cache` | +| `--log-level` | choice | Log verbosity: `DEBUG`, `INFO`, `WARNING`, `ERROR` | `WARNING` | Example: diff --git a/hatch/cli/__main__.py b/hatch/cli/__main__.py index f1d6e98..18aa11b 100644 --- a/hatch/cli/__main__.py +++ b/hatch/cli/__main__.py @@ -972,7 +972,7 @@ def main() -> int: """ # Configure logging logging.basicConfig( - level=logging.INFO, + level=logging.WARNING, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", ) @@ -1010,8 +1010,15 @@ def main() -> int: default=Path.home() / ".hatch" / "cache", help="Directory to store cached packages", ) + parser.add_argument( + "--log-level", + default="WARNING", + choices=["DEBUG", "INFO", "WARNING", "ERROR"], + help="Log verbosity level (default: WARNING)", + ) args = parser.parse_args() + logging.getLogger().setLevel(getattr(logging, args.log_level)) # Initialize managers (lazy - only when needed) from hatch.environment_manager import HatchEnvironmentManager diff --git a/hatch/environment_manager.py b/hatch/environment_manager.py index 19a5e6f..70413a0 100644 --- a/hatch/environment_manager.py +++ b/hatch/environment_manager.py @@ -62,7 +62,6 @@ def __init__( """ self.logger = logging.getLogger("hatch.environment_manager") - self.logger.setLevel(logging.INFO) # Set up environment directories self.environments_dir = environments_dir or (Path.home() / ".hatch" / "envs") self.environments_dir.mkdir(exist_ok=True) diff --git a/hatch/installers/dependency_installation_orchestrator.py b/hatch/installers/dependency_installation_orchestrator.py index 3be72ba..13ecac1 100644 --- a/hatch/installers/dependency_installation_orchestrator.py +++ b/hatch/installers/dependency_installation_orchestrator.py @@ -67,7 +67,6 @@ def __init__( registry_data (Dict[str, Any]): Registry data for dependency resolution. """ self.logger = logging.getLogger("hatch.dependency_orchestrator") - self.logger.setLevel(logging.INFO) self.package_loader = package_loader self.registry_service = registry_service self.registry_data = registry_data diff --git a/hatch/installers/docker_installer.py b/hatch/installers/docker_installer.py index e3a7da7..150a2dc 100644 --- a/hatch/installers/docker_installer.py +++ b/hatch/installers/docker_installer.py @@ -20,7 +20,6 @@ from .registry import installer_registry logger = logging.getLogger("hatch.installers.docker_installer") -logger.setLevel(logging.INFO) # Handle docker-py import with graceful fallback DOCKER_AVAILABLE = False diff --git a/hatch/installers/python_installer.py b/hatch/installers/python_installer.py index 420471c..70419d2 100644 --- a/hatch/installers/python_installer.py +++ b/hatch/installers/python_installer.py @@ -31,7 +31,6 @@ class PythonInstaller(DependencyInstaller): def __init__(self): """Initialize the PythonInstaller.""" self.logger = logging.getLogger("hatch.installers.python_installer") - self.logger.setLevel(logging.INFO) @property def installer_type(self) -> str: diff --git a/hatch/installers/system_installer.py b/hatch/installers/system_installer.py index 95820bc..1c0f475 100644 --- a/hatch/installers/system_installer.py +++ b/hatch/installers/system_installer.py @@ -33,7 +33,6 @@ class SystemInstaller(DependencyInstaller): def __init__(self): """Initialize the SystemInstaller.""" self.logger = logging.getLogger("hatch.installers.system_installer") - self.logger.setLevel(logging.INFO) @property def installer_type(self) -> str: diff --git a/hatch/package_loader.py b/hatch/package_loader.py index 5ee3a33..24c59db 100644 --- a/hatch/package_loader.py +++ b/hatch/package_loader.py @@ -31,7 +31,6 @@ def __init__(self, cache_dir: Optional[Path] = None): Defaults to ~/.hatch/packages. """ self.logger = logging.getLogger("hatch.package_loader") - self.logger.setLevel(logging.INFO) # Set up cache directory if cache_dir is None: diff --git a/hatch/python_environment_manager.py b/hatch/python_environment_manager.py index 5b4936d..c139f4f 100644 --- a/hatch/python_environment_manager.py +++ b/hatch/python_environment_manager.py @@ -41,7 +41,6 @@ def __init__(self, environments_dir: Optional[Path] = None): Defaults to ~/.hatch/envs. """ self.logger = logging.getLogger("hatch.python_environment_manager") - self.logger.setLevel(logging.INFO) # Set up environment directories self.environments_dir = environments_dir or (Path.home() / ".hatch" / "envs") diff --git a/hatch/registry_retriever.py b/hatch/registry_retriever.py index d905484..f276a6a 100644 --- a/hatch/registry_retriever.py +++ b/hatch/registry_retriever.py @@ -6,12 +6,23 @@ import json import logging +import sys import requests import datetime from pathlib import Path from typing import Dict, Any, Optional +def _print_registry_status(msg: str) -> None: + if sys.stderr.isatty(): + print(f"\033[2m{msg}\033[0m", end="\r", file=sys.stderr, flush=True) + + +def _clear_registry_status() -> None: + if sys.stderr.isatty(): + print(" " * 60, end="\r", file=sys.stderr, flush=True) + + class RegistryRetriever: """Manages the retrieval and caching of the Hatch package registry. @@ -37,7 +48,6 @@ def __init__( local_registry_cache_path (Path, optional): Path to local registry file. Defaults to None. """ self.logger = logging.getLogger("hatch.registry_retriever") - self.logger.setLevel(logging.INFO) self.cache_ttl = cache_ttl self.simulation_mode = simulation_mode @@ -59,7 +69,7 @@ def __init__( # Use file:// URL format for local files self.registry_url = f"file://{str(self.registry_cache_path.absolute())}" - self.logger.info( + self.logger.debug( f"Operating in simulation mode with registry at: {self.registry_cache_path}" ) else: @@ -69,7 +79,7 @@ def __init__( # We'll set the initial URL to today, but might fall back to yesterday self.registry_url = f"https://github.com/CrackingShells/Hatch-Registry/releases/download/{self.today_str}/hatch_packages_registry.json" - self.logger.info( + self.logger.debug( f"Operating in online mode with registry at: {self.registry_url}" ) @@ -180,7 +190,7 @@ def _fetch_remote_registry(self) -> Dict[str, Any]: """ if self.simulation_mode: try: - self.logger.info(f"Fetching registry from {self.registry_url}") + self.logger.debug(f"Fetching registry from {self.registry_url}") with open(self.registry_cache_path, "r") as f: return json.load(f) except Exception as e: @@ -193,7 +203,7 @@ def _fetch_remote_registry(self) -> Dict[str, Any]: self.registry_url = f"https://github.com/CrackingShells/Hatch-Registry/releases/download/{date}/hatch_packages_registry.json" self.is_delayed = False # Reset delayed flag for today's registry else: - self.logger.info( + self.logger.warning( f"Today's registry ({date}) not found, falling back to yesterday's" ) # Fall back to yesterday's registry @@ -211,7 +221,7 @@ def _fetch_remote_registry(self) -> Dict[str, Any]: self.is_delayed = True # Set delayed flag for yesterday's registry try: - self.logger.info(f"Fetching registry from {self.registry_url}") + self.logger.debug(f"Fetching registry from {self.registry_url}") response = requests.get(self.registry_url, timeout=30) response.raise_for_status() return response.json() @@ -298,8 +308,9 @@ def get_registry(self, force_refresh: bool = False) -> Dict[str, Any]: # In simulation mode, we must have a local registry file registry_data = self._read_local_cache() else: - # In online mode, fetch from remote URL + _print_registry_status(" Refreshing registry cache...") registry_data = self._fetch_remote_registry() + _clear_registry_status() # Update local cache # Note that in case of simulation mode AND default cache path,