butler/main.py
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env python3
"""Butler CLI entry point — thin orchestrator.
R1-7 refactor: this module is now a thin dispatch layer. Subcommand
registration and per-command logic live in ``butler/cli/<area>_cli.py``
modules (chat_cli, projects_cli, memory_cli, runtime_cli, gateway_cli,
mcp_cli, plus the pre-existing registry_cli / sessions_cli /
skills_registry / workflow_cli / experiment_cli / prompt_eval_cli /
provider_presets_cli / secrets_cli). Slash commands are dispatched by
``butler.cli.slash_dispatch``.
Audit source: ``docs/reviews/project-deep-audit-2026-06-r1to8.md`` §R1-7
"""
from __future__ import annotations
import argparse
import logging
from typing import TYPE_CHECKING, Sequence
from butler.cli.blackboard_cli import register_blackboard_parser
from butler.cli.chat_cli import register_chat_parser
from butler.cli.cost_cli import register_cost_parser
from butler.cli.doctor import cmd_doctor
from butler.cli.events_cli import register_events_parser
from butler.cli.eval_cli import register_eval_parser
from butler.cli.architecture_cli import register_architecture_parser
from butler.cli.experiment_cli import register_experiment_parser
from butler.cli.gateway_cli import register_gateway_parser
from butler.cli.gate_cli import register_gate_parser
from butler.cli.mcp_cli import register_mcp_parser
from butler.cli.memory_cli import register_memory_parser
from butler.cli.onboard_cli import register_onboard_parser
from butler.cli.projects_cli import register_projects_parser
from butler.cli.prompt_eval_cli import register_prompt_eval_parser
from butler.cli.provider_presets_cli import register_provider_presets_parser
from butler.cli.registry_cli import register_registry_parser
from butler.cli.runtime_cli import register_runtime_parser
from butler.cli.secrets_cli import register_secrets_subparser
from butler.cli.sessions_cli import register_sessions_subparser
from butler.cli.skills_registry import register_skills_parser
from butler.cli.transcript_cli import register_transcript_parser
from butler.cli.workflow_cli import register_workflow_subparser
from butler.utilities.env_parse import init_dotenv
from butler.utilities.logging_config import configure_logging
if TYPE_CHECKING:
pass
logger = logging.getLogger(__name__)
def _build_parser() -> argparse.ArgumentParser:
"""Build the top-level ``butler`` argument parser.
R1-7 refactor: this is a thin orchestrator that delegates to
per-area ``register_xxx_parser(sub)`` helpers. Adding a new
subcommand is a single ``register_xxx_parser(sub)`` call in the
appropriate ``butler.cli.<area>_cli`` module.
"""
p = argparse.ArgumentParser(
prog="butler",
description="Butler v4 — AI 管家系统(自建 Agent Loop 架构)",
)
sub = p.add_subparsers(dest="command", required=True)
_register_per_area_parsers(sub)
_register_preexisting_parsers(sub)
sub.add_parser(
"doctor",
help="静态安全配置审计(OpenClaw doctor 子集,只读)",
).set_defaults(func=_cmd_doctor)
return p
def _register_per_area_parsers(sub: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
"""Per-area registrations (R1-7 extraction targets)."""
register_chat_parser(sub)
register_cost_parser(sub)
register_events_parser(sub)
register_architecture_parser(sub)
register_onboard_parser(sub)
register_projects_parser(sub)
register_memory_parser(sub)
register_runtime_parser(sub)
register_gateway_parser(sub)
register_gate_parser(sub)
register_mcp_parser(sub)
register_blackboard_parser(sub)
def _register_preexisting_parsers(sub: argparse._SubParsersAction[argparse.ArgumentParser]) -> None:
"""Pre-existing delegations — keep them here so the orchestrator
surface is the single point of truth for "what does `butler` do?"."""
register_eval_parser(sub)
register_skills_parser(sub)
register_workflow_subparser(sub)
register_experiment_parser(sub)
register_sessions_subparser(sub)
register_transcript_parser(sub)
register_prompt_eval_parser(sub)
register_provider_presets_parser(sub)
register_registry_parser(sub)
register_secrets_subparser(sub)
def _cmd_doctor(ns: argparse.Namespace) -> int:
return int(cmd_doctor(ns))
def main(argv: Sequence[str] | None = None) -> None:
init_dotenv()
configure_logging()
# Initialize event consumers for real-time monitoring and analytics
from butler.core.events.event_consumer import initialize_event_consumers
initialize_event_consumers()
args = _build_parser().parse_args(argv)
code = args.func(args)
raise SystemExit(code)
# ---------------------------------------------------------------------------
# Backward-compat re-exports
#
# R1-7 moved the per-area command logic into ``butler.cli.<area>_cli``.
# Existing tests and the cli_harness import these private names from
# ``butler.main``; we re-export them here so the import surface stays
# stable. New code should import from the per-area modules directly.
# ---------------------------------------------------------------------------
# chat + exec + interactive loop
# project subcommands
# memory subcommands
# runtime subcommands
# gateway + wechat-setup
# mcp serve (the catalog subcommands live in mcp_catalog_cli)
# slash dispatcher + session helpers
if __name__ == "__main__":
main()