1"""ll-issues next-action: Print the next refinement action needed across all active issues."""

2

3 from __future__ import annotations

4

5 import argparse

6 import json

7 from typing import TYPE_CHECKING

8

9 if TYPE_CHECKING:

10 from little_loops.config import BRConfig

11

12

13 def cmd_next_action(config: BRConfig, args: argparse.Namespace) -> int:

14 """Print the next refinement action needed across all active issues.

15

16 Output: NEEDS_FORMAT|NEEDS_VERIFY|NEEDS_SCORE|NEEDS_REFINE <id> (exit 1)

17 ALL_DONE (exit 0)

18

19 Args:

20 config: Project configuration

21 args: Parsed arguments with optional .refine_cap, .ready_threshold, .outcome_threshold

22

23 Returns:

24 Exit code (1 = work remains, 0 = all done)

25 """

26 from little_loops.cli_args import parse_issue_ids

27 from little_loops.issue_parser import find_issues, is_formatted

28

29 skip_ids = parse_issue_ids(getattr(args, "skip", None))

30 issues = find_issues(config, skip_ids=skip_ids or None)

31 issues.sort(key=lambda i: (i.priority_int, -int(i.issue_id.split("-")[1])))

32

33 refine_cap: int = getattr(args, "refine_cap", 5)

34 default_ready: int = getattr(args, "ready_threshold", 85)

35 default_outcome: int = getattr(args, "outcome_threshold", 70)

36

37 config_path = config.project_root / ".ll" / "ll-config.json"

38 try:

39 cg = json.loads(config_path.read_text()).get("commands", {}).get("confidence_gate", {})

40 ready_threshold: int = cg.get("readiness_threshold", default_ready)

41 outcome_threshold: int = cg.get("outcome_threshold", default_outcome)

42 except Exception:

43 ready_threshold = default_ready

44 outcome_threshold = default_outcome

45

46 for issue in issues:

47 if not is_formatted(issue.path):

48 print(f"NEEDS_FORMAT {issue.issue_id}")

49 return 1

50 if "/ll:verify-issues" not in issue.session_commands:

51 print(f"NEEDS_VERIFY {issue.issue_id}")

52 return 1

53 cs = issue.confidence_score

54 oc = issue.outcome_confidence

55 if cs is None or oc is None:

56 print(f"NEEDS_SCORE {issue.issue_id}")

57 return 1

58 if issue.session_command_counts.get("/ll:refine-issue", 0) < refine_cap:

59 if cs < ready_threshold or oc < outcome_threshold:

60 print(f"NEEDS_REFINE {issue.issue_id}")

61 return 1

62

63 print("ALL_DONE")

64 return 0