Coverage for little_loops / config / cli.py: 100%

72 statements  

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

1"""CLI presentation configuration dataclasses. 

2 

3Covers ANSI color overrides for log levels, priority labels, type labels, 

4and general CLI display options. 

5""" 

6 

7from __future__ import annotations 

8 

9from dataclasses import dataclass, field 

10from typing import Any 

11 

12 

13@dataclass 

14class CliColorsLoggerConfig: 

15 """ANSI color overrides for Logger log-level output.""" 

16 

17 info: str = "36" 

18 success: str = "32" 

19 warning: str = "33" 

20 error: str = "38;5;208" 

21 

22 @classmethod 

23 def from_dict(cls, data: dict[str, Any]) -> CliColorsLoggerConfig: 

24 """Create CliColorsLoggerConfig from dictionary.""" 

25 return cls( 

26 info=data.get("info", "36"), 

27 success=data.get("success", "32"), 

28 warning=data.get("warning", "33"), 

29 error=data.get("error", "38;5;208"), 

30 ) 

31 

32 

33@dataclass 

34class CliColorsPriorityConfig: 

35 """ANSI color overrides for issue priority labels (P0–P5).""" 

36 

37 P0: str = "38;5;208;1" 

38 P1: str = "38;5;208" 

39 P2: str = "33" 

40 P3: str = "0" 

41 P4: str = "2" 

42 P5: str = "2" 

43 

44 @classmethod 

45 def from_dict(cls, data: dict[str, Any]) -> CliColorsPriorityConfig: 

46 """Create CliColorsPriorityConfig from dictionary.""" 

47 return cls( 

48 P0=data.get("P0", "38;5;208;1"), 

49 P1=data.get("P1", "38;5;208"), 

50 P2=data.get("P2", "33"), 

51 P3=data.get("P3", "0"), 

52 P4=data.get("P4", "2"), 

53 P5=data.get("P5", "2"), 

54 ) 

55 

56 

57@dataclass 

58class CliColorsTypeConfig: 

59 """ANSI color overrides for issue type labels (BUG, FEAT, ENH, EPIC).""" 

60 

61 BUG: str = "38;5;208" 

62 FEAT: str = "32" 

63 ENH: str = "34" 

64 EPIC: str = "35" 

65 

66 @classmethod 

67 def from_dict(cls, data: dict[str, Any]) -> CliColorsTypeConfig: 

68 """Create CliColorsTypeConfig from dictionary.""" 

69 return cls( 

70 BUG=data.get("BUG", "38;5;208"), 

71 FEAT=data.get("FEAT", "32"), 

72 ENH=data.get("ENH", "34"), 

73 EPIC=data.get("EPIC", "35"), 

74 ) 

75 

76 

77@dataclass 

78class CliColorsEdgeLabelsConfig: 

79 """ANSI color overrides for FSM transition edge labels in loop diagrams.""" 

80 

81 yes: str = "32" 

82 no: str = "38;5;208" 

83 error: str = "31" 

84 partial: str = "33" 

85 next: str = "2" 

86 default: str = "2" 

87 blocked: str = "31" 

88 retry_exhausted: str = "38;5;208" 

89 rate_limit_exhausted: str = "38;5;214" 

90 

91 @classmethod 

92 def from_dict(cls, data: dict[str, Any]) -> CliColorsEdgeLabelsConfig: 

93 """Create CliColorsEdgeLabelsConfig from dictionary.""" 

94 return cls( 

95 yes=data.get("yes", "32"), 

96 no=data.get("no", "38;5;208"), 

97 error=data.get("error", "31"), 

98 partial=data.get("partial", "33"), 

99 next=data.get("next", "2"), 

100 default=data.get("default", "2"), 

101 blocked=data.get("blocked", "31"), 

102 retry_exhausted=data.get("retry_exhausted", "38;5;208"), 

103 rate_limit_exhausted=data.get("rate_limit_exhausted", "38;5;214"), 

104 ) 

105 

106 def to_dict(self) -> dict[str, str]: 

107 """Convert to a label→SGR-code dict for use by _colorize_diagram_labels. 

108 

109 Maps 'default' back to '_' to match the key used in _EDGE_LABEL_COLORS. 

110 """ 

111 return { 

112 "yes": self.yes, 

113 "no": self.no, 

114 "error": self.error, 

115 "partial": self.partial, 

116 "next": self.next, 

117 "_": self.default, 

118 "blocked": self.blocked, 

119 "retry_exhausted": self.retry_exhausted, 

120 "rate_limit_exhausted": self.rate_limit_exhausted, 

121 } 

122 

123 

124@dataclass 

125class CliColorsConfig: 

126 """ANSI color overrides for logger levels, priority labels, and type labels.""" 

127 

128 logger: CliColorsLoggerConfig = field(default_factory=CliColorsLoggerConfig) 

129 priority: CliColorsPriorityConfig = field(default_factory=CliColorsPriorityConfig) 

130 type: CliColorsTypeConfig = field(default_factory=CliColorsTypeConfig) 

131 fsm_active_state: str = "32" 

132 fsm_edge_labels: CliColorsEdgeLabelsConfig = field(default_factory=CliColorsEdgeLabelsConfig) 

133 

134 @classmethod 

135 def from_dict(cls, data: dict[str, Any]) -> CliColorsConfig: 

136 """Create CliColorsConfig from dictionary.""" 

137 return cls( 

138 logger=CliColorsLoggerConfig.from_dict(data.get("logger", {})), 

139 priority=CliColorsPriorityConfig.from_dict(data.get("priority", {})), 

140 type=CliColorsTypeConfig.from_dict(data.get("type", {})), 

141 fsm_active_state=data.get("fsm_active_state", "32"), 

142 fsm_edge_labels=CliColorsEdgeLabelsConfig.from_dict(data.get("fsm_edge_labels", {})), 

143 ) 

144 

145 

146@dataclass 

147class RefineStatusConfig: 

148 """refine-status display configuration.""" 

149 

150 columns: list[str] = field(default_factory=list) 

151 elide_order: list[str] = field(default_factory=list) 

152 

153 @classmethod 

154 def from_dict(cls, data: dict[str, Any]) -> RefineStatusConfig: 

155 """Create RefineStatusConfig from dictionary.""" 

156 return cls( 

157 columns=data.get("columns", []), 

158 elide_order=data.get("elide_order", []), 

159 ) 

160 

161 

162@dataclass 

163class CliConfig: 

164 """CLI output configuration.""" 

165 

166 color: bool = True 

167 colors: CliColorsConfig = field(default_factory=CliColorsConfig) 

168 

169 @classmethod 

170 def from_dict(cls, data: dict[str, Any]) -> CliConfig: 

171 """Create CliConfig from dictionary.""" 

172 return cls( 

173 color=data.get("color", True), 

174 colors=CliColorsConfig.from_dict(data.get("colors", {})), 

175 )