Skip to content

Commit f87ea11

Browse files
committed
feat(cli): add new CLI entry point for balatrollm`
1 parent 2540725 commit f87ea11

1 file changed

Lines changed: 102 additions & 0 deletions

File tree

src/balatrollm/cli.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
"""CLI entry point for balatrollm command."""
2+
3+
import argparse
4+
import asyncio
5+
import sys
6+
from pathlib import Path
7+
8+
from .config import Config, Task
9+
10+
11+
def create_parser() -> argparse.ArgumentParser:
12+
"""Create the argument parser."""
13+
parser = argparse.ArgumentParser(
14+
prog="balatrollm",
15+
description="LLM-powered Balatro bot",
16+
)
17+
18+
# Positional config file (optional)
19+
parser.add_argument(
20+
"config",
21+
nargs="?",
22+
type=Path,
23+
help="Config file path (YAML)",
24+
)
25+
26+
# Game params (nargs='+' for lists)
27+
parser.add_argument("--model", nargs="+", help="Model(s) to use")
28+
parser.add_argument("--seed", nargs="+", help="Game seed(s)")
29+
parser.add_argument("--deck", nargs="+", help="Deck code(s): RED, BLUE, etc.")
30+
parser.add_argument("--stake", nargs="+", help="Stake code(s): WHITE, GOLD, etc.")
31+
parser.add_argument("--strategy", nargs="+", help="Strategy name(s)")
32+
33+
# Execution
34+
parser.add_argument("--parallel", type=int, help="Concurrent instances")
35+
36+
# Connection
37+
parser.add_argument("--host", help="BalatroBot host")
38+
parser.add_argument("--port", type=int, help="Starting port")
39+
parser.add_argument("--base-url", help="LLM API base URL")
40+
parser.add_argument("--api-key", help="LLM API key")
41+
42+
# CLI-only
43+
parser.add_argument("--dry-run", action="store_true", help="Show tasks only")
44+
45+
return parser
46+
47+
48+
def print_tasks(tasks: list[Task]) -> None:
49+
"""Print task list for dry run."""
50+
total = len(tasks)
51+
for i, task in enumerate(tasks, 1):
52+
print(f"[{i:0{len(str(total))}d}/{total}] {task}")
53+
54+
55+
async def execute(config: Config, tasks: list[Task]) -> int:
56+
"""Execute all tasks. Returns exit code."""
57+
from .executor import Executor
58+
59+
executor = Executor(config=config, tasks=tasks)
60+
61+
try:
62+
await executor.run()
63+
return 0
64+
except (KeyboardInterrupt, asyncio.CancelledError):
65+
print("\nInterrupted by user")
66+
return 130
67+
except Exception as e:
68+
print(f"Execution failed: {e}")
69+
return 1
70+
71+
72+
def main() -> None:
73+
"""Main entry point for balatrollm command."""
74+
parser = create_parser()
75+
args = parser.parse_args()
76+
77+
# Build config with precedence: env < yaml < args
78+
try:
79+
config = Config.load(yaml_path=args.config, args=args)
80+
config.validate()
81+
except (FileNotFoundError, ValueError) as e:
82+
parser.error(str(e))
83+
84+
# Generate tasks
85+
tasks = config.generate_tasks()
86+
87+
# Dry run?
88+
if args.dry_run:
89+
print_tasks(tasks)
90+
return
91+
92+
# Execute
93+
try:
94+
exit_code = asyncio.run(execute(config, tasks))
95+
sys.exit(exit_code)
96+
except KeyboardInterrupt:
97+
print("\nInterrupted by user")
98+
sys.exit(130)
99+
100+
101+
if __name__ == "__main__":
102+
main()

0 commit comments

Comments
 (0)