Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions refactron/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,43 @@ def _print_refactor_messages(summary: dict, preview: bool) -> None:
console.print("\n[green]✅ Refactoring completed! Don't forget to test your code.[/green]")


def _print_operation_with_risk(operation) -> None:
"""Print refactoring operation with color-coded risk visualization."""
from rich.panel import Panel

from refactron.core.risk_assessment import RiskAssessor

# Determine risk level and color using centralized logic
assessor = RiskAssessor()
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new RiskAssessor instance is created in _print_operation_with_risk for every operation printed. If this function is called multiple times (e.g., in a loop for many operations), it creates unnecessary objects. Consider accepting a RiskAssessor instance as a parameter or creating it once in the calling code and reusing it.

Copilot uses AI. Check for mistakes.
risk_score = operation.risk_score
risk_color = assessor.get_risk_display_color(risk_score)
risk_label = assessor.get_risk_display_label(risk_score)

# Build content
content = f"[bold]{operation.operation_type}[/bold]\n"
content += f"Location: {operation.file_path}:{operation.line_number}\n"
content += f"Risk: [{risk_color}]{risk_score:.2f} - {risk_label}[/{risk_color}]\n"
content += f"\n{operation.description}"

# Add risk factors if available
if "risk_factors" in operation.metadata:
risk_factors = operation.metadata["risk_factors"]
content += "\n\n[bold]Risk Breakdown:[/bold]\n"
content += f" • Impact Scope: {risk_factors.get('impact_scope', 0):.2f}\n"
content += f" • Change Type: {risk_factors.get('change_type_risk', 0):.2f}\n"
content += f" • Test Coverage: {risk_factors.get('test_coverage_risk', 0):.2f}\n"
content += f" • Dependencies: {risk_factors.get('dependency_risk', 0):.2f}\n"
content += f" • Complexity: {risk_factors.get('complexity_risk', 0):.2f}"

if risk_factors.get("test_file_exists"):
content += "\n\n✓ Test file exists"
else:
content += "\n\n⚠ No test file found - higher risk"

panel = Panel(content, border_style=risk_color, expand=False)
console.print(panel)

Comment on lines +148 to +183
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function _print_operation_with_risk is defined but never called anywhere in the codebase. The refactor command at line 309 uses result.show_diff() instead. Either this function should be integrated into the CLI workflow (replacing or complementing show_diff), or it should be removed if it's not needed. If it's intended for future use, consider adding a TODO comment explaining when and how it should be used.

Copilot uses AI. Check for mistakes.

@click.group()
@click.version_option(version="1.0.0")
def main() -> None:
Expand Down
43 changes: 41 additions & 2 deletions refactron/core/refactor_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def operations_by_type(self, operation_type: str) -> List[RefactoringOperation]:
return [op for op in self.operations if op.operation_type == operation_type]

def show_diff(self) -> str:
"""Show a diff of all operations."""
"""Show a diff of all operations with detailed risk assessment."""
lines = []
lines.append("=" * 80)
lines.append("REFACTORING PREVIEW")
Expand All @@ -54,7 +54,39 @@ def show_diff(self) -> str:
lines.append("-" * 80)
lines.append(f"Operation {i}: {op.operation_type}")
lines.append(f"Location: {op.file_path}:{op.line_number}")
lines.append(f"Risk Score: {op.risk_score:.2f}")

# Show risk with visual indicator
risk_icon = self._get_risk_icon(op.risk_score)
lines.append(f"Risk Score: {op.risk_score:.2f} {risk_icon}")

# Show detailed risk factors if available
if "risk_factors" in op.metadata:
lines.append("")
lines.append(" Risk Breakdown:")
risk_factors = op.metadata["risk_factors"]
lines.append(f" • Impact Scope: {risk_factors.get('impact_scope', 0):.2f}")
lines.append(
f" • Change Type Risk: {risk_factors.get('change_type_risk', 0):.2f}"
)
lines.append(
f" • Test Coverage Risk: {risk_factors.get('test_coverage_risk', 0):.2f}"
)
lines.append(f" • Dependency Risk: {risk_factors.get('dependency_risk', 0):.2f}")
lines.append(f" • Complexity Risk: {risk_factors.get('complexity_risk', 0):.2f}")

# Show affected components
if risk_factors.get("affected_functions"):
lines.append(
f" • Affected Functions: {', '.join(risk_factors['affected_functions'])}"
)

# Show test coverage status
if risk_factors.get("test_file_exists"):
lines.append(" • Test Coverage: ✓ Test file exists")
else:
lines.append(" • Test Coverage: ⚠ No test file found")

lines.append("")
lines.append(f"Description: {op.description}")

if op.reasoning:
Expand All @@ -74,6 +106,13 @@ def show_diff(self) -> str:
lines.append("=" * 80)
return "\n".join(lines)

def _get_risk_icon(self, risk_score: float) -> str:
"""Get visual indicator for risk level."""
from refactron.core.risk_assessment import RiskAssessor

assessor = RiskAssessor()
return assessor.get_risk_display_label(risk_score)
Comment on lines +109 to +114
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new RiskAssessor instance is created every time _get_risk_icon is called, which happens for each operation in show_diff. Since show_diff can iterate over many operations (line 53), this could create many unnecessary RiskAssessor objects. Consider creating a single RiskAssessor instance at the class level or reusing one instance within the show_diff method.

Copilot uses AI. Check for mistakes.

def apply(self) -> bool:
"""Apply the refactoring operations (placeholder)."""
# This would actually apply the changes to files
Expand Down
Loading
Loading