Coverage for little_loops / cli_args.py: 30%
77 statements
« prev ^ index » next coverage.py v7.12.0, created at 2026-03-18 16:20 -0500
« prev ^ index » next coverage.py v7.12.0, created at 2026-03-18 16:20 -0500
1"""Shared CLI argument definitions for little-loops tools.
3Provides reusable functions for adding common command-line arguments
4to argparse parsers, ensuring consistency across ll-auto, ll-parallel,
5and ll-sprint commands.
6"""
8from __future__ import annotations
10import argparse
11from pathlib import Path
14def add_dry_run_arg(parser: argparse.ArgumentParser) -> None:
15 """Add --dry-run/-n argument to parser."""
16 parser.add_argument(
17 "--dry-run",
18 "-n",
19 action="store_true",
20 help="Show what would be done without making changes",
21 )
24def add_resume_arg(parser: argparse.ArgumentParser) -> None:
25 """Add --resume/-r argument to parser."""
26 parser.add_argument(
27 "--resume",
28 "-r",
29 action="store_true",
30 help="Resume from previous checkpoint",
31 )
34def add_config_arg(parser: argparse.ArgumentParser) -> None:
35 """Add --config argument to parser."""
36 parser.add_argument(
37 "--config",
38 type=Path,
39 default=None,
40 help="Path to project root (default: current directory)",
41 )
44def add_only_arg(parser: argparse.ArgumentParser) -> None:
45 """Add --only argument for filtering specific issues."""
46 parser.add_argument(
47 "--only",
48 type=str,
49 default=None,
50 help="Comma-separated list of issue IDs to process (e.g., BUG-001,FEAT-002)",
51 )
54def add_skip_arg(parser: argparse.ArgumentParser, help_text: str | None = None) -> None:
55 """Add --skip argument for excluding specific issues.
57 Args:
58 parser: The argument parser to add the argument to
59 help_text: Optional custom help text. If not provided, uses default.
60 """
61 if help_text is None:
62 help_text = "Comma-separated list of issue IDs to skip (e.g., BUG-003,FEAT-004)"
63 parser.add_argument(
64 "--skip",
65 type=str,
66 default=None,
67 help=help_text,
68 )
71def add_max_workers_arg(parser: argparse.ArgumentParser, default: int | None = None) -> None:
72 """Add --max-workers/-w argument for parallel execution.
74 Args:
75 parser: The argument parser to add the argument to
76 default: Default value. If None, no default is specified.
77 """
78 if default is not None:
79 parser.add_argument(
80 "--max-workers",
81 "-w",
82 type=int,
83 default=default,
84 help=f"Maximum parallel workers (default: {default})",
85 )
86 else:
87 parser.add_argument(
88 "--max-workers",
89 "-w",
90 type=int,
91 default=None,
92 help="Maximum parallel workers",
93 )
96def add_timeout_arg(parser: argparse.ArgumentParser, default: int | None = None) -> None:
97 """Add --timeout/-t argument for per-issue timeout.
99 Args:
100 parser: The argument parser to add the argument to
101 default: Default value in seconds. If None, no default is specified.
102 """
103 if default is not None:
104 parser.add_argument(
105 "--timeout",
106 "-t",
107 type=int,
108 default=default,
109 help=f"Timeout in seconds (default: {default})",
110 )
111 else:
112 parser.add_argument(
113 "--timeout",
114 "-t",
115 type=int,
116 default=None,
117 help="Timeout in seconds",
118 )
121def add_idle_timeout_arg(parser: argparse.ArgumentParser) -> None:
122 """Add --idle-timeout argument for idle process termination.
124 Args:
125 parser: The argument parser to add the argument to
126 """
127 parser.add_argument(
128 "--idle-timeout",
129 type=int,
130 default=None,
131 help="Kill worker if no output for N seconds (0 to disable, default: from config)",
132 )
135def add_handoff_threshold_arg(parser: argparse.ArgumentParser) -> None:
136 """Add --handoff-threshold argument for per-run context handoff override.
138 Args:
139 parser: The argument parser to add the argument to
140 """
141 parser.add_argument(
142 "--handoff-threshold",
143 type=int,
144 default=None,
145 help="Override auto-handoff context threshold (1-100, default: from config)",
146 )
149def add_quiet_arg(parser: argparse.ArgumentParser) -> None:
150 """Add --quiet/-q argument to suppress output."""
151 parser.add_argument(
152 "--quiet",
153 "-q",
154 action="store_true",
155 help="Suppress non-essential output",
156 )
159def add_skip_analysis_arg(parser: argparse.ArgumentParser) -> None:
160 """Add --skip-analysis argument to skip dependency discovery."""
161 parser.add_argument(
162 "--skip-analysis",
163 action="store_true",
164 help="Skip dependency analysis (use when dependencies are known to be current)",
165 )
168def add_max_issues_arg(parser: argparse.ArgumentParser) -> None:
169 """Add --max-issues/-m argument for limiting issues processed."""
170 parser.add_argument(
171 "--max-issues",
172 "-m",
173 type=int,
174 default=0,
175 help="Limit number of issues to process (0 = unlimited)",
176 )
179def parse_issue_ids(value: str | None) -> set[str] | None:
180 """Parse comma-separated issue IDs into a set.
182 Args:
183 value: Comma-separated string like "BUG-001,FEAT-002" or None
185 Returns:
186 Set of uppercase issue IDs, or None if value is None
188 Example:
189 >>> parse_issue_ids("BUG-001,feat-002")
190 {'BUG-001', 'FEAT-002'}
191 >>> parse_issue_ids(None)
192 None
193 """
194 if value is None:
195 return None
196 return {i.strip().upper() for i in value.split(",")}
199def parse_issue_ids_ordered(value: str | None) -> list[str] | None:
200 """Parse comma-separated issue IDs into an ordered list.
202 Like parse_issue_ids but preserves input order, enabling callers to
203 honor the sequence in which IDs were specified.
205 Args:
206 value: Comma-separated string like "BUG-001,FEAT-002" or None
208 Returns:
209 List of uppercase issue IDs in input order, or None if value is None
211 Example:
212 >>> parse_issue_ids_ordered("BUG-010,FEAT-005,ENH-020")
213 ['BUG-010', 'FEAT-005', 'ENH-020']
214 >>> parse_issue_ids_ordered(None)
215 None
216 """
217 if value is None:
218 return None
219 return [i.strip().upper() for i in value.split(",")]
222VALID_ISSUE_TYPES = {"BUG", "FEAT", "ENH"}
225def add_type_arg(parser: argparse.ArgumentParser) -> None:
226 """Add --type argument for filtering issues by type prefix."""
227 parser.add_argument(
228 "--type",
229 type=str,
230 default=None,
231 help="Comma-separated issue types to process (e.g., BUG, FEAT, ENH)",
232 )
235def parse_issue_types(value: str | None) -> set[str] | None:
236 """Parse comma-separated issue types into a validated set.
238 Args:
239 value: Comma-separated string like "BUG,ENH" or None
241 Returns:
242 Set of uppercase type prefixes, or None if value is None
244 Raises:
245 SystemExit: If invalid issue types are provided (via argparse error)
247 Example:
248 >>> parse_issue_types("bug,enh")
249 {'BUG', 'ENH'}
250 >>> parse_issue_types(None)
251 None
252 """
253 if value is None:
254 return None
255 types = {t.strip().upper() for t in value.split(",")}
256 invalid = types - VALID_ISSUE_TYPES
257 if invalid:
258 import sys
260 print(
261 f"error: invalid issue type(s): {', '.join(sorted(invalid))}. "
262 f"Valid types: {', '.join(sorted(VALID_ISSUE_TYPES))}",
263 file=sys.stderr,
264 )
265 sys.exit(2)
266 return types
269def add_common_auto_args(parser: argparse.ArgumentParser) -> None:
270 """Add arguments common to ll-auto command.
272 Adds: --resume, --dry-run, --max-issues, --quiet, --only, --skip, --type, --config,
273 --idle-timeout, --handoff-threshold
274 """
275 add_resume_arg(parser)
276 add_dry_run_arg(parser)
277 add_max_issues_arg(parser)
278 add_quiet_arg(parser)
279 add_only_arg(parser)
280 add_skip_arg(parser)
281 add_type_arg(parser)
282 add_config_arg(parser)
283 add_idle_timeout_arg(parser)
284 add_handoff_threshold_arg(parser)
287def add_common_parallel_args(parser: argparse.ArgumentParser) -> None:
288 """Add arguments common to parallel execution tools.
290 Adds: --dry-run, --resume, --max-workers, --timeout, --idle-timeout, --quiet, --only, --skip, --type, --config
291 """
292 add_dry_run_arg(parser)
293 add_resume_arg(parser)
294 add_max_workers_arg(parser)
295 add_timeout_arg(parser)
296 add_idle_timeout_arg(parser)
297 add_quiet_arg(parser)
298 add_only_arg(parser)
299 add_skip_arg(parser)
300 add_type_arg(parser)
301 add_config_arg(parser)
304__all__ = [
305 "add_dry_run_arg",
306 "add_resume_arg",
307 "add_config_arg",
308 "add_only_arg",
309 "add_skip_arg",
310 "add_type_arg",
311 "add_max_workers_arg",
312 "add_timeout_arg",
313 "add_idle_timeout_arg",
314 "add_handoff_threshold_arg",
315 "add_quiet_arg",
316 "add_skip_analysis_arg",
317 "add_max_issues_arg",
318 "parse_issue_ids",
319 "parse_issue_types",
320 "VALID_ISSUE_TYPES",
321 "add_common_auto_args",
322 "add_common_parallel_args",
323]