1"""CLI presentation configuration dataclasses.

2

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

4 and general CLI display options.

5"""

6

7 from __future__ import annotations

8

9 from dataclasses import dataclass, field

10 from typing import Any

11

12

13@dataclass

14 class 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

34 class 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

58 class 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

78 class 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

125 class 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

147 class 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

163 class 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 )