#!/usr/bin/env python3 """ Shared smoke test runner for hermes-agent. Fast checks that catch obvious breakage without maintenance burden. Usage as CLI: python -m devkit.smoke_test python -m devkit.smoke_test --verbose Usage as module: from devkit.smoke_test import run_smoke_tests results = run_smoke_tests() """ import argparse import importlib import json import subprocess import sys from pathlib import Path from typing import Any, Dict, List HERMES_ROOT = Path(__file__).resolve().parent.parent def _test_imports() -> Dict[str, Any]: modules = [ "hermes_constants", "hermes_state", "cli", "tools.skills_sync", "tools.skills_hub", ] errors = [] for mod in modules: try: importlib.import_module(mod) except Exception as e: errors.append({"module": mod, "error": str(e)}) return { "name": "core_imports", "status": "ok" if not errors else "fail", "errors": errors, } def _test_cli_entrypoints() -> Dict[str, Any]: entrypoints = [ [sys.executable, "-m", "cli", "--help"], ] errors = [] for cmd in entrypoints: try: subprocess.run(cmd, capture_output=True, text=True, check=True, cwd=HERMES_ROOT) except subprocess.CalledProcessError as e: errors.append({"cmd": cmd, "error": f"exit {e.returncode}"}) except Exception as e: errors.append({"cmd": cmd, "error": str(e)}) return { "name": "cli_entrypoints", "status": "ok" if not errors else "fail", "errors": errors, } def _test_green_path_e2e() -> Dict[str, Any]: """One bare green-path E2E: terminal_tool echo hello.""" try: from tools.terminal_tool import terminal result = terminal(command="echo hello") output = result.get("output", "") if "hello" in output.lower(): return {"name": "green_path_e2e", "status": "ok", "output": output.strip()} return {"name": "green_path_e2e", "status": "fail", "error": f"Unexpected output: {output}"} except Exception as e: return {"name": "green_path_e2e", "status": "fail", "error": str(e)} def run_smoke_tests(verbose: bool = False) -> Dict[str, Any]: tests = [ _test_imports(), _test_cli_entrypoints(), _test_green_path_e2e(), ] failed = [t for t in tests if t["status"] != "ok"] result = { "overall": "ok" if not failed else "fail", "tests": tests, "failed_count": len(failed), } if verbose: print(json.dumps(result, indent=2)) return result def main(argv: List[str] = None) -> int: argv = argv or sys.argv[1:] parser = argparse.ArgumentParser(description="Smoke test runner") parser.add_argument("--verbose", action="store_true") args = parser.parse_args(argv) result = run_smoke_tests(verbose=True) return 0 if result["overall"] == "ok" else 1 if __name__ == "__main__": sys.exit(main())