Coverage for little_loops / cli / issues / skip.py: 69%

36 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2026-05-22 16:19 -0500

1"""ll-issues skip: Deprioritize an issue by bumping its priority prefix.""" 

2 

3from __future__ import annotations 

4 

5import re 

6import sys 

7from typing import TYPE_CHECKING 

8 

9if TYPE_CHECKING: 

10 import argparse 

11 

12 from little_loops.config import BRConfig 

13 

14 

15def cmd_skip(config: BRConfig, args: argparse.Namespace) -> int: 

16 """Deprioritize an issue by renaming its priority prefix. 

17 

18 Renames the issue file to the given priority (default P5), appends a 

19 ``## Skip Log`` entry with timestamp and optional reason, and prints the 

20 new file path to stdout so callers can confirm the rename. 

21 

22 Args: 

23 config: Project configuration 

24 args: Parsed arguments with .issue_id, .priority, and .reason 

25 

26 Returns: 

27 Exit code (0 = success, 1 = error) 

28 """ 

29 from little_loops.cli.issues.show import _resolve_issue_id 

30 from little_loops.issue_lifecycle import skip_issue 

31 from little_loops.issue_parser import IssueParser 

32 

33 path = _resolve_issue_id(config, args.issue_id) 

34 if path is None: 

35 print(f"Error: Issue '{args.issue_id}' not found.", file=sys.stderr) 

36 return 1 

37 

38 # Only skip non-terminal issues (check frontmatter status, not directory) 

39 issue_info = IssueParser(config).parse_file(path) 

40 if issue_info.status in ("done", "cancelled", "deferred"): 

41 print( 

42 f"Error: Issue '{args.issue_id}' has status '{issue_info.status}', not an active issue.", 

43 file=sys.stderr, 

44 ) 

45 return 1 

46 

47 new_name = re.sub(r"^P\d-", f"{args.priority}-", path.name) 

48 new_path = path.parent / new_name 

49 

50 if path == new_path: 

51 # Already at target priority — nothing to rename, still print path 

52 try: 

53 rel = str(new_path.relative_to(config.project_root)) 

54 except ValueError: 

55 rel = str(new_path) 

56 print(f"Deprioritized {args.issue_id} to {args.priority}: {rel}") 

57 return 0 

58 

59 try: 

60 skip_issue(path, new_path, args.reason) 

61 except FileExistsError as exc: 

62 print(f"Error: {exc}", file=sys.stderr) 

63 return 1 

64 

65 try: 

66 rel = str(new_path.relative_to(config.project_root)) 

67 except ValueError: 

68 rel = str(new_path) 

69 

70 print(f"Deprioritized {args.issue_id} to {args.priority}: {rel}") 

71 return 0