Coverage for lintro / utils / console / pre_execution_summary.py: 77%

82 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Pre-execution configuration summary display. 

2 

3Shows the effective configuration (tools, auto-install, environment) 

4before tools begin execution, giving users transparency into what 

5lintro decided before it does anything. 

6""" 

7 

8from __future__ import annotations 

9 

10from typing import TYPE_CHECKING 

11 

12from rich.console import Console 

13from rich.table import Table 

14 

15if TYPE_CHECKING: 

16 from lintro.ai.config import AIConfig 

17 from lintro.utils.execution.tool_configuration import SkippedTool 

18 

19 

20def print_pre_execution_summary( 

21 *, 

22 tools_to_run: list[str], 

23 skipped_tools: list[SkippedTool], 

24 effective_auto_install: bool, 

25 is_container: bool, 

26 is_ci: bool, 

27 per_tool_auto_install: dict[str, bool | None] | None = None, 

28 ai_config: AIConfig | None = None, 

29) -> None: 

30 """Print a pre-execution configuration summary table. 

31 

32 Args: 

33 tools_to_run: List of tool names that will execute. 

34 skipped_tools: List of tools skipped with reasons. 

35 effective_auto_install: Global effective auto-install setting. 

36 is_container: Whether running in a container environment. 

37 is_ci: Whether running in a CI environment. 

38 per_tool_auto_install: Per-tool auto-install overrides. 

39 ai_config: AI configuration, if available. 

40 """ 

41 console = Console() 

42 

43 table = Table( 

44 title="Configuration", 

45 title_style="bold cyan", 

46 show_header=True, 

47 header_style="bold", 

48 border_style="dim", 

49 padding=(0, 1), 

50 ) 

51 

52 table.add_column("Setting", style="cyan", no_wrap=True) 

53 table.add_column("Value") 

54 

55 # Environment 

56 env_parts: list[str] = [] 

57 if is_container: 

58 env_parts.append("[bold]Container[/bold]") 

59 if is_ci: 

60 env_parts.append("CI") 

61 if not env_parts: 

62 env_parts.append("Local") 

63 table.add_row("Environment", ", ".join(env_parts)) 

64 

65 # Auto-install default 

66 if effective_auto_install: 

67 auto_source = "" 

68 if is_container: 

69 auto_source = " (container default)" 

70 auto_str = f"[green]enabled{auto_source}[/green]" 

71 else: 

72 auto_str = "[dim]disabled[/dim]" 

73 table.add_row("Auto-install", auto_str) 

74 

75 # Tools to run 

76 if tools_to_run: 

77 tool_lines: list[str] = [] 

78 per_tool = per_tool_auto_install or {} 

79 for name in tools_to_run: 

80 override = per_tool.get(name) 

81 if override is True: 

82 tool_lines.append( 

83 f"{name} [green](auto-install: on)[/green]", 

84 ) 

85 elif override is False: 

86 tool_lines.append( 

87 f"{name} [dim](auto-install: off)[/dim]", 

88 ) 

89 else: 

90 tool_lines.append(f"{name}") 

91 table.add_row("Tools", "\n".join(tool_lines)) 

92 else: 

93 table.add_row("Tools", "[dim]None (all tools skipped)[/dim]") 

94 

95 # Skipped tools 

96 if skipped_tools: 

97 skip_lines = [ 

98 f" • [yellow]{st.name}[/yellow] [dim]({st.reason})[/dim]" 

99 for st in skipped_tools 

100 ] 

101 table.add_row("Skipped", "\n".join(skip_lines)) 

102 

103 # AI configuration (always render for visibility). 

104 ai_parts: list[str] = [] 

105 if ai_config is None: 

106 ai_parts.append("[dim]disabled (no config)[/dim]") 

107 elif not ai_config.enabled: 

108 ai_parts.append("[dim]disabled[/dim]") 

109 else: 

110 import os 

111 

112 from lintro.ai.availability import is_provider_available 

113 from lintro.ai.providers import get_default_model 

114 from lintro.ai.registry import PROVIDERS, AIProvider 

115 

116 provider_name = ai_config.provider.lower() 

117 supported = set(AIProvider) 

118 

119 # Check: unknown provider 

120 if provider_name not in supported: 

121 ai_parts.append("[red]enabled (unknown provider)[/red]") 

122 names = ", ".join(sorted(supported)) 

123 ai_parts.append( 

124 f" [yellow]'{ai_config.provider}' is not supported. " 

125 f"Use: {names}[/yellow]", 

126 ) 

127 else: 

128 # Check SDK availability 

129 sdk_ok = is_provider_available(provider_name) 

130 

131 # Check API key 

132 key_env = ai_config.api_key_env or PROVIDERS.default_api_key_envs.get( 

133 AIProvider(provider_name), 

134 "", 

135 ) 

136 key_set = bool(os.environ.get(key_env)) if key_env else False 

137 

138 if sdk_ok and key_set: 

139 ai_parts.append("[green]enabled[/green]") 

140 elif not sdk_ok: 

141 ai_parts.append( 

142 "[red]enabled (SDK not installed)[/red]", 

143 ) 

144 ai_parts.append( 

145 " [yellow]run: uv pip install 'lintro\\[ai]'[/yellow]", 

146 ) 

147 elif not key_set: 

148 ai_parts.append( 

149 "[yellow]enabled (API key missing)[/yellow]", 

150 ) 

151 ai_parts.append( 

152 f" [yellow]set {key_env} env var[/yellow]", 

153 ) 

154 

155 ai_parts.append(f" provider: {ai_config.provider}") 

156 

157 effective_model = ai_config.model or get_default_model( 

158 provider_name, 

159 ) 

160 if effective_model: 

161 model_label = effective_model 

162 if not ai_config.model: 

163 model_label += " [dim](default)[/dim]" 

164 ai_parts.append(f" model: {model_label}") 

165 

166 # auto_apply warning 

167 if ai_config.auto_apply: 

168 if is_ci: 

169 ai_parts.append(" auto-apply: [green]on[/green]") 

170 else: 

171 ai_parts.append( 

172 " auto-apply: [bold red]on (files will be " 

173 "modified without confirmation)[/bold red]", 

174 ) 

175 

176 # Parallel workers 

177 ai_parts.append( 

178 f" parallel: {ai_config.max_parallel_calls} workers", 

179 ) 

180 ai_parts.append( 

181 " safe-auto-apply: " 

182 + ( 

183 "[green]on[/green]" 

184 if ai_config.auto_apply_safe_fixes 

185 else "[dim]off[/dim]" 

186 ), 

187 ) 

188 ai_parts.append( 

189 " verify-fixes: " 

190 + ( 

191 "[green]on[/green]" 

192 if ai_config.validate_after_group 

193 else "[dim]off[/dim]" 

194 ), 

195 ) 

196 

197 table.add_row("AI", "\n".join(ai_parts)) 

198 

199 console.print(table) 

200 console.print()