From 5d109ea290695a149fe2b542a69e74285d5de55d Mon Sep 17 00:00:00 2001 From: claude Date: Fri, 24 Jul 2026 03:47:32 +0000 Subject: [PATCH] CLI cleanup: remove info, rename list subcommands (#472) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove `info` command - Rename `list active` → `active` (new top-level command) - Rename `list available` → `list` (no subcommand argument) Co-Authored-By: Claude Sonnet 4.6 --- bot_bottle/backend/__init__.py | 8 ++-- bot_bottle/backend/docker/enumerate.py | 4 +- bot_bottle/cli/__init__.py | 10 ++--- bot_bottle/cli/active.py | 53 ++++++++++++++++++++++++ bot_bottle/cli/commit.py | 2 +- bot_bottle/cli/info.py | 49 ---------------------- bot_bottle/cli/list.py | 56 ++++---------------------- 7 files changed, 72 insertions(+), 110 deletions(-) create mode 100644 bot_bottle/cli/active.py delete mode 100644 bot_bottle/cli/info.py diff --git a/bot_bottle/backend/__init__.py b/bot_bottle/backend/__init__.py index ff5223e..5052cf1 100644 --- a/bot_bottle/backend/__init__.py +++ b/bot_bottle/backend/__init__.py @@ -21,7 +21,7 @@ backend exposes five methods: enumerate_active() -> Sequence[ActiveAgent] Return every currently-running bottle on this backend, with - enough metadata for callers (CLI `list active`, dashboard + enough metadata for callers (CLI `active`, dashboard agents pane) to render a row. Selection is driven by `--backend` on `start` or BOT_BOTTLE_BACKEND @@ -200,7 +200,7 @@ class ExecResult: @dataclass(frozen=True) class ActiveAgent: - """One currently-running agent, as the CLI `list active` and + """One currently-running agent, as the CLI `active` and dashboard agents pane render it. ("Agent" is the project's consistent name for the thing running inside a bottle — the bottle is the container, the agent is what runs in it.) @@ -593,7 +593,7 @@ class BottleBackend(ABC, Generic[PlanT, CleanupT]): Linux + KVM. Used by the cross-backend `enumerate_active_agents` / `cmd_cleanup` to skip backends the operator hasn't installed, so a docker-only host - doesn't fail when `cli.py list active` walks past + doesn't fail when `cli.py active` walks past firecracker.""" @classmethod @@ -813,7 +813,7 @@ def has_backend(name: str) -> bool: def enumerate_active_agents() -> list[ActiveAgent]: """All currently-running agents, across every available - backend. Used by CLI `list active` and the dashboard's agents + backend. Used by CLI `active` and the dashboard's agents pane so neither has to know which backends exist. Skips backends whose `is_available()` reports False. diff --git a/bot_bottle/backend/docker/enumerate.py b/bot_bottle/backend/docker/enumerate.py index 0337d5a..8bfd91c 100644 --- a/bot_bottle/backend/docker/enumerate.py +++ b/bot_bottle/backend/docker/enumerate.py @@ -1,6 +1,6 @@ """Active-agent enumeration for the docker backend. -Returns `ActiveAgent` records the CLI `list active` command and the +Returns `ActiveAgent` records the CLI `active` command and the dashboard agents pane consume. Empty when docker isn't reachable — gated by `has_backend('docker')` at the cross-backend caller so this module trusts that docker is available when called. @@ -60,7 +60,7 @@ def _parse_services_by_project(stdout: str) -> dict[str, set[str]]: def _query_services_by_project() -> dict[str, set[str]]: """One `docker ps` call → `{project: {service, ...}}`. Used - by the CLI's `list active` and the dashboard's agents pane — + by the CLI's `active` and the dashboard's agents pane — one subprocess per refresh tick, not one per bottle.""" try: r = subprocess.run( diff --git a/bot_bottle/cli/__init__.py b/bot_bottle/cli/__init__.py index cf62f68..98470de 100644 --- a/bot_bottle/cli/__init__.py +++ b/bot_bottle/cli/__init__.py @@ -1,6 +1,6 @@ """Main CLI dispatcher. -Commands: backend, cleanup, commit, edit, info, init, list, resume, start, supervise +Commands: active, backend, cleanup, commit, edit, init, list, resume, start, supervise """ from __future__ import annotations @@ -13,11 +13,11 @@ from ..manifest import ManifestError from ..store_manager import StoreManager from ._common import PROG from . import list as _list_mod +from .active import cmd_active from .backend import cmd_backend from .cleanup import cmd_cleanup from .commit import cmd_commit from .edit import cmd_edit -from .info import cmd_info from .init import cmd_init from .login import cmd_login from .resume import cmd_resume @@ -27,11 +27,11 @@ from .supervise import cmd_supervise cmd_list = _list_mod.cmd_list COMMANDS = { + "active": cmd_active, "backend": cmd_backend, "cleanup": cmd_cleanup, "commit": cmd_commit, "edit": cmd_edit, - "info": cmd_info, "init": cmd_init, "list": cmd_list, "login": cmd_login, @@ -51,13 +51,13 @@ NO_MIGRATION_COMMANDS = frozenset({"backend", "login"}) def usage() -> None: sys.stderr.write(f"usage: {PROG} [args...]\n\n") sys.stderr.write("Commands:\n") + sys.stderr.write(" active list currently-running bot-bottle bottles\n") sys.stderr.write(" backend set up / check / undo a backend's host prerequisites (setup|status|teardown)\n") sys.stderr.write(" cleanup stop and remove all active bot-bottle containers\n") sys.stderr.write(" commit snapshot a running bottle's container state to a Docker image\n") sys.stderr.write(" edit open an agent in vim for editing\n") - sys.stderr.write(" info print env, skills, and prompt details for a named agent\n") sys.stderr.write(" init interactively create a new agent and add it to bot-bottle.json\n") - sys.stderr.write(" list list available agents or active containers\n") + sys.stderr.write(" list list available agents from bot-bottle.json\n") sys.stderr.write(" login register this host with a bot-bottle console\n") sys.stderr.write( " resume re-launch a bottle by its identity " diff --git a/bot_bottle/cli/active.py b/bot_bottle/cli/active.py new file mode 100644 index 0000000..97e3229 --- /dev/null +++ b/bot_bottle/cli/active.py @@ -0,0 +1,53 @@ +"""active: list currently-running bot-bottle bottles.""" + +from __future__ import annotations + +import os +import sys + +from ..backend import enumerate_active_agents +from ._common import PROG + +_ANSI_COLOR_CODES: dict[str, str] = { + "red": "\033[91m", + "green": "\033[92m", + "yellow": "\033[93m", + "blue": "\033[94m", + "magenta": "\033[95m", +} +_ANSI_RESET = "\033[0m" + + +def _ansi_label(text: str, color: str) -> str: + if not color: + return text + if not sys.stdout.isatty(): + return text + term = os.environ.get("TERM", "") + if term in ("dumb", ""): + return text + code = _ANSI_COLOR_CODES.get(color) + if not code: + return text + return f"{code}{text}{_ANSI_RESET}" + + +def cmd_active(argv: list[str]) -> int: + if argv and argv[0] in ("-h", "--help"): + sys.stderr.write(f"usage: {PROG} active\n") + sys.stderr.write("\nList all currently-running bot-bottle bottles.\n") + sys.stderr.write("Output: \\t\\t