feat: polish exec cli ergonomics and output
This commit is contained in:
@@ -23,6 +23,41 @@ MODULE_MAP = {
|
||||
"auto-trader": "auto_trader",
|
||||
}
|
||||
|
||||
ALIASES = {
|
||||
"api-check": "check-api",
|
||||
"diag": "doctor",
|
||||
"gate": "external-gate",
|
||||
"probe": "market-probe",
|
||||
"review": "review-context",
|
||||
"recap": "review-engine",
|
||||
"rotate-gate-log": "rotate-external-gate-log",
|
||||
"rotate-log": "rotate-external-gate-log",
|
||||
"exec": "smart-executor",
|
||||
}
|
||||
|
||||
COMMAND_HELP = [
|
||||
("api-check", "check-api", "Validate exchange/API connectivity"),
|
||||
("diag", "doctor", "Inspect runtime wiring and diagnostics"),
|
||||
("gate", "external-gate", "Run external gate orchestration"),
|
||||
("init", None, "Initialize user runtime state"),
|
||||
("paths", None, "Print runtime path resolution"),
|
||||
("precheck", None, "Run precheck workflow"),
|
||||
("probe", "market-probe", "Query external market data"),
|
||||
("review", "review-context", "Generate review context"),
|
||||
("recap", "review-engine", "Generate review recap/engine output"),
|
||||
("rotate-gate-log, rotate-log", "rotate-external-gate-log", "Rotate external gate logs"),
|
||||
("exec", "smart-executor", "Trading and execution actions"),
|
||||
("auto-trader", None, "Auto trader entrypoint"),
|
||||
]
|
||||
|
||||
|
||||
def _command_listing() -> str:
|
||||
lines = []
|
||||
for names, canonical, summary in COMMAND_HELP:
|
||||
label = names if canonical is None else f"{names} (alias for {canonical})"
|
||||
lines.append(f" {label:<45} {summary}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
class VersionAction(argparse.Action):
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
@@ -36,34 +71,46 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
description="CoinHunter trading operations CLI",
|
||||
formatter_class=argparse.RawTextHelpFormatter,
|
||||
epilog=(
|
||||
"Commands:\n"
|
||||
f"{_command_listing()}\n\n"
|
||||
"Examples:\n"
|
||||
" coinhunter doctor\n"
|
||||
" coinhunter diag\n"
|
||||
" coinhunter paths\n"
|
||||
" coinhunter check-api\n"
|
||||
" coinhunter smart-executor balances\n"
|
||||
" coinhunter smart-executor hold\n"
|
||||
" coinhunter smart-executor --analysis '...' --reasoning '...' buy ENJUSDT 50\n"
|
||||
" coinhunter api-check\n"
|
||||
" coinhunter exec bal\n"
|
||||
" coinhunter exec overview\n"
|
||||
" coinhunter exec hold\n"
|
||||
" coinhunter exec --analysis '...' --reasoning '...' buy ENJUSDT 50\n"
|
||||
" coinhunter precheck\n"
|
||||
" coinhunter precheck --ack '分析完成:HOLD'\n"
|
||||
" coinhunter external-gate\n"
|
||||
" coinhunter review-context 12\n"
|
||||
" coinhunter market-probe bybit-ticker BTCUSDT\n"
|
||||
" coinhunter gate\n"
|
||||
" coinhunter review 12\n"
|
||||
" coinhunter recap 12\n"
|
||||
" coinhunter probe bybit-ticker BTCUSDT\n"
|
||||
" coinhunter init\n"
|
||||
"\n"
|
||||
"Preferred exec verbs are bal, overview, hold, buy, flat, and rotate.\n"
|
||||
"Legacy command names remain supported for backward compatibility.\n"
|
||||
),
|
||||
)
|
||||
parser.add_argument("--version", nargs=0, action=VersionAction, help="Print installed version and exit")
|
||||
parser.add_argument("command", nargs="?", choices=sorted(MODULE_MAP.keys()), help="CoinHunter command to run")
|
||||
parser.add_argument(
|
||||
"command",
|
||||
nargs="?",
|
||||
metavar="COMMAND",
|
||||
help="Command to run. Use --help to see canonical names and short aliases.",
|
||||
)
|
||||
parser.add_argument("args", nargs=argparse.REMAINDER)
|
||||
return parser
|
||||
|
||||
|
||||
def run_python_module(module_name: str, argv: list[str]) -> int:
|
||||
def run_python_module(module_name: str, argv: list[str], display_name: str) -> int:
|
||||
module = importlib.import_module(f".{module_name}", package="coinhunter")
|
||||
if not hasattr(module, "main"):
|
||||
raise RuntimeError(f"Module {module_name} has no main()")
|
||||
old_argv = sys.argv[:]
|
||||
try:
|
||||
sys.argv = [f"coinhunter {module_name}", *argv]
|
||||
sys.argv = [display_name, *argv]
|
||||
result = module.main()
|
||||
return int(result) if isinstance(result, int) else 0
|
||||
except SystemExit as exc:
|
||||
@@ -78,11 +125,16 @@ def main() -> int:
|
||||
if not parsed.command:
|
||||
parser.print_help()
|
||||
return 0
|
||||
module_name = MODULE_MAP[parsed.command]
|
||||
command = ALIASES.get(parsed.command, parsed.command)
|
||||
if command not in MODULE_MAP:
|
||||
parser.error(
|
||||
f"invalid command: {parsed.command!r}. Use `coinhunter --help` to see supported commands and aliases."
|
||||
)
|
||||
module_name = MODULE_MAP[command]
|
||||
argv = list(parsed.args)
|
||||
if argv and argv[0] == "--":
|
||||
argv = argv[1:]
|
||||
return run_python_module(module_name, argv)
|
||||
return run_python_module(module_name, argv, f"coinhunter {parsed.command}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user