From c3e119e3c9d3b2549d3bda4226f8162b625d98dc Mon Sep 17 00:00:00 2001 From: Zhengshou Lai Date: Sun, 9 Aug 2026 15:08:03 +0800 Subject: [PATCH] feat: offer backend CLI install on missing binary (entrypoints) + shared backend_available/ensure_backend helpers - Standalone my* wrappers pass offer_install=True so a missing backend CLI triggers the interactive 'Install now? [y/N]' prompt (was a bare not-found). - Add backend_available()/ensure_backend() to expose the backend registry (binary/env_bin/install_cmd) for xiaohe init / ensure-deps to reuse. --- myagents/entrypoints.py | 13 ++++++++----- myagents/launcher.py | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/myagents/entrypoints.py b/myagents/entrypoints.py index e463b8e..517b7fb 100644 --- a/myagents/entrypoints.py +++ b/myagents/entrypoints.py @@ -2,11 +2,14 @@ from myagents.launcher import build_cli -claude_cli = build_cli("claude", prog_name="myclaude") -kimi_cli = build_cli("kimi", prog_name="mykimi") -codex_cli = build_cli("codex", prog_name="mycodex") -hermes_cli = build_cli("hermes", prog_name="myhermes") -cursor_cli = build_cli("cursor", prog_name="mycursor") +# offer_install=True so a missing backend CLI (e.g. `claude`) triggers the +# interactive "Install now? [y/N]" prompt instead of a bare not-found error. +# Backends without an install_cmd (hermes/cursor) fall back to the manual msg. +claude_cli = build_cli("claude", prog_name="myclaude", offer_install=True) +kimi_cli = build_cli("kimi", prog_name="mykimi", offer_install=True) +codex_cli = build_cli("codex", prog_name="mycodex", offer_install=True) +hermes_cli = build_cli("hermes", prog_name="myhermes", offer_install=True) +cursor_cli = build_cli("cursor", prog_name="mycursor", offer_install=True) def _progs(): diff --git a/myagents/launcher.py b/myagents/launcher.py index 6ea4e81..0e50529 100644 --- a/myagents/launcher.py +++ b/myagents/launcher.py @@ -256,6 +256,28 @@ def _offer_install(config: dict) -> str | None: return _resolve_backend_binary(config) +def backend_available(backend: str) -> bool: + """True if the backend CLI (or its env override) resolves on PATH.""" + cfg = _BACKENDS.get(backend) + return bool(cfg and _resolve_backend_binary(cfg)) + + +def ensure_backend(backend: str) -> bool: + """Ensure a backend CLI is available, offering to install it when missing. + + Reuses the interactive ``_offer_install`` (prompt on tty, install via the + backend's ``install_cmd``). Backends without an installer (e.g. hermes) + fall back to the manual not-found message. Returns True if the CLI is + available afterwards. + """ + cfg = _BACKENDS.get(backend) + if not cfg: + return False + if _resolve_backend_binary(cfg): + return True + return _offer_install(cfg) is not None + + def _launch( backend: str, chat_cwd: Path,