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
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Pre-execution configuration summary display.
3Shows the effective configuration (tools, auto-install, environment)
4before tools begin execution, giving users transparency into what
5lintro decided before it does anything.
6"""
8from __future__ import annotations
10from typing import TYPE_CHECKING
12from rich.console import Console
13from rich.table import Table
15if TYPE_CHECKING:
16 from lintro.ai.config import AIConfig
17 from lintro.utils.execution.tool_configuration import SkippedTool
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.
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()
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 )
52 table.add_column("Setting", style="cyan", no_wrap=True)
53 table.add_column("Value")
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))
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)
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]")
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))
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
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
116 provider_name = ai_config.provider.lower()
117 supported = set(AIProvider)
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)
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
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 )
155 ai_parts.append(f" provider: {ai_config.provider}")
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}")
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 )
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 )
197 table.add_row("AI", "\n".join(ai_parts))
199 console.print(table)
200 console.print()