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

2

3 from __future__ import annotations

4

5 import argparse

6 from typing import TYPE_CHECKING

7

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

9

10 if TYPE_CHECKING:

11 from little_loops.config import BRConfig

12

13

14 def 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 and optional .type attributes

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 type_prefixes = {args.type} if getattr(args, "type", None) else None

28 issues = find_issues(config, type_prefixes=type_prefixes)

29

30 if not issues:

31 print("No active issues found.")

32 return 0

33

34 graph = DependencyGraph.from_issues(issues)

35

36 try:

37 ordered = graph.topological_sort()

38 except ValueError as exc:

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

40 ordered = issues # fall back to priority order

41

42 limit = args.limit

43 shown = ordered[:limit]

44

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

46 type_filter = getattr(args, "type", None)

47 print_json(

48 [

49 {

50 "id": issue.issue_id,

51 "priority": issue.priority,

52 "title": issue.title,

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

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

55 "blocks": issue.blocks,

56 **({"type_filter": type_filter} if type_filter else {}),

57 }

58 for issue in shown

59 ]

60 )

61 return 0

62

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

64 for issue in shown:

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

66 if blockers:

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

68 else:

69 rationale = "no blockers"

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

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

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

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

74

75 if len(ordered) > limit:

76 remaining = len(ordered) - limit

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

78

79 return 0