Coverage for little_loops / cli / issues / sequence.py: 0%

36 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2026-03-18 16:18 -0500

1"""ll-issues sequence: Suggest dependency-ordered implementation sequence.""" 

2 

3from __future__ import annotations 

4 

5import argparse 

6from typing import TYPE_CHECKING 

7 

8from little_loops.cli.output import PRIORITY_COLOR, TYPE_COLOR, colorize, print_json 

9 

10if TYPE_CHECKING: 

11 from little_loops.config import BRConfig 

12 

13 

14def cmd_sequence(config: BRConfig, args: argparse.Namespace) -> int: 

15 """Output a dependency-ordered list of active issues with rationale. 

16 

17 Args: 

18 config: Project configuration 

19 args: Parsed arguments with .limit attribute 

20 

21 Returns: 

22 Exit code (0 = success) 

23 """ 

24 from little_loops.dependency_graph import DependencyGraph 

25 from little_loops.issue_parser import find_issues 

26 

27 issues = find_issues(config) 

28 

29 if not issues: 

30 print("No active issues found.") 

31 return 0 

32 

33 graph = DependencyGraph.from_issues(issues) 

34 

35 try: 

36 ordered = graph.topological_sort() 

37 except ValueError as exc: 

38 print(f"Warning: dependency cycle detected — {exc}") 

39 ordered = issues # fall back to priority order 

40 

41 limit = args.limit 

42 shown = ordered[:limit] 

43 

44 if getattr(args, "json", False): 

45 print_json( 

46 [ 

47 { 

48 "id": issue.issue_id, 

49 "priority": issue.priority, 

50 "title": issue.title, 

51 "path": str(issue.path), 

52 "blocked_by": sorted(graph.blocked_by.get(issue.issue_id, set())), 

53 "blocks": issue.blocks, 

54 } 

55 for issue in shown 

56 ] 

57 ) 

58 return 0 

59 

60 print(f"Suggested implementation sequence ({len(shown)} of {len(ordered)} issues):\n") 

61 for issue in shown: 

62 blockers = graph.blocked_by.get(issue.issue_id, set()) 

63 if blockers: 

64 rationale = f"blocked by: {', '.join(sorted(blockers))}" 

65 else: 

66 rationale = "no blockers" 

67 issue_prefix = issue.issue_id.split("-", 1)[0] 

68 colored_id = colorize(issue.issue_id, TYPE_COLOR.get(issue_prefix, "0")) 

69 colored_pri = colorize(issue.priority, PRIORITY_COLOR.get(issue.priority, "0")) 

70 print(f" [{colored_pri}, {rationale}] {colored_id}: {issue.title}") 

71 

72 if len(ordered) > limit: 

73 remaining = len(ordered) - limit 

74 print(f"\n … +{remaining} more (use --limit to show more)") 

75 

76 return 0