Ctrl K
ComunidadGuías…

butler/main.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/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()