crackerjack 0.37.8__py3-none-any.whl → 0.38.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of crackerjack might be problematic. Click here for more details.

crackerjack/__init__.py CHANGED
@@ -30,7 +30,7 @@ from .errors import (
30
30
  check_file_exists,
31
31
  handle_error,
32
32
  )
33
- from .interactive import WorkflowOptions
33
+ from .interactive import InteractiveWorkflowOptions as WorkflowOptions
34
34
 
35
35
  try:
36
36
  from importlib.metadata import version
crackerjack/api.py CHANGED
@@ -8,8 +8,7 @@ from rich.console import Console
8
8
  from .code_cleaner import CleaningResult, CodeCleaner, PackageCleaningResult
9
9
  from .core.workflow_orchestrator import WorkflowOrchestrator
10
10
  from .errors import CrackerjackError, ErrorCode
11
- from .interactive import InteractiveCLI
12
- from .interactive import WorkflowOptions as InteractiveWorkflowOptions
11
+ from .interactive import InteractiveCLI, InteractiveWorkflowOptions
13
12
  from .models.config import WorkflowOptions
14
13
  from .services.regex_patterns import SAFE_PATTERNS
15
14
 
@@ -67,10 +67,8 @@ class WorkflowPipeline:
67
67
 
68
68
  # Initialize quality intelligence for advanced decision making
69
69
  try:
70
- quality_baseline = EnhancedQualityBaselineService(console, pkg_path)
71
- self._quality_intelligence = QualityIntelligenceService(
72
- quality_baseline, console
73
- )
70
+ quality_baseline = EnhancedQualityBaselineService()
71
+ self._quality_intelligence = QualityIntelligenceService(quality_baseline)
74
72
  except Exception:
75
73
  # Fallback gracefully if quality intelligence is not available
76
74
  self._quality_intelligence = None
@@ -171,22 +169,33 @@ class WorkflowPipeline:
171
169
 
172
170
  def _initialize_zuban_lsp(self, options: OptionsProtocol) -> None:
173
171
  """Initialize Zuban LSP server if not disabled."""
174
- # Check if LSP is disabled via CLI flag or configuration
172
+ if self._should_skip_zuban_lsp(options):
173
+ return
174
+
175
+ if self._is_zuban_lsp_already_running():
176
+ return
177
+
178
+ self._start_zuban_lsp_server(options)
179
+
180
+ def _should_skip_zuban_lsp(self, options: OptionsProtocol) -> bool:
181
+ """Check if Zuban LSP server should be skipped."""
175
182
  if getattr(options, "no_zuban_lsp", False):
176
183
  self.logger.debug("Zuban LSP server disabled by --no-zuban-lsp flag")
177
- return
184
+ return True
178
185
 
179
- # Get configuration from options (will use config system if available)
180
186
  config = getattr(options, "zuban_lsp", None)
181
187
  if config and not config.enabled:
182
188
  self.logger.debug("Zuban LSP server disabled in configuration")
183
- return
189
+ return True
184
190
 
185
191
  if config and not config.auto_start:
186
192
  self.logger.debug("Zuban LSP server auto-start disabled in configuration")
187
- return
193
+ return True
194
+
195
+ return False
188
196
 
189
- # Check if LSP server is already running to avoid duplicates
197
+ def _is_zuban_lsp_already_running(self) -> bool:
198
+ """Check if LSP server is already running to avoid duplicates."""
190
199
  from crackerjack.services.server_manager import find_zuban_lsp_processes
191
200
 
192
201
  existing_processes = find_zuban_lsp_processes()
@@ -194,20 +203,17 @@ class WorkflowPipeline:
194
203
  self.logger.debug(
195
204
  f"Zuban LSP server already running (PID: {existing_processes[0]['pid']})"
196
205
  )
197
- return
206
+ return True
207
+ return False
198
208
 
199
- # Auto-start LSP server in background
209
+ def _start_zuban_lsp_server(self, options: OptionsProtocol) -> None:
210
+ """Start the Zuban LSP server in background."""
200
211
  try:
201
212
  import subprocess
202
213
  import sys
203
214
 
204
- # Use configuration values if available, otherwise fallback to CLI options
205
- if config:
206
- zuban_lsp_port = config.port
207
- zuban_lsp_mode = config.mode
208
- else:
209
- zuban_lsp_port = getattr(options, "zuban_lsp_port", 8677)
210
- zuban_lsp_mode = getattr(options, "zuban_lsp_mode", "stdio")
215
+ config = getattr(options, "zuban_lsp", None)
216
+ zuban_lsp_port, zuban_lsp_mode = self._get_zuban_lsp_config(options, config)
211
217
 
212
218
  cmd = [
213
219
  sys.executable,
@@ -234,6 +240,17 @@ class WorkflowPipeline:
234
240
  except Exception as e:
235
241
  self.logger.warning(f"Failed to auto-start Zuban LSP server: {e}")
236
242
 
243
+ def _get_zuban_lsp_config(
244
+ self, options: OptionsProtocol, config: any
245
+ ) -> tuple[int, str]:
246
+ """Get Zuban LSP configuration values."""
247
+ if config:
248
+ return config.port, config.mode
249
+ return (
250
+ getattr(options, "zuban_lsp_port", 8677),
251
+ getattr(options, "zuban_lsp_mode", "stdio"),
252
+ )
253
+
237
254
  def _log_zuban_lsp_status(self) -> None:
238
255
  """Display current Zuban LSP server status during workflow startup."""
239
256
  from crackerjack.services.server_manager import find_zuban_lsp_processes
@@ -353,36 +370,9 @@ class WorkflowPipeline:
353
370
  return
354
371
 
355
372
  try:
356
- # Gather performance metrics from the workflow execution
357
- {
358
- "workflow_id": workflow_id,
359
- "total_duration": duration,
360
- "success": success,
361
- "cache_metrics": self._cache.get_stats() if self._cache else {},
362
- "memory_metrics": self._memory_optimizer.get_stats()
363
- if hasattr(self._memory_optimizer, "get_stats")
364
- else {},
365
- }
366
-
367
- # Generate benchmark comparison
373
+ self._gather_performance_metrics(workflow_id, duration, success)
368
374
  benchmark_results = await self._performance_benchmarks.run_benchmark_suite()
369
-
370
- # Display compact performance summary
371
- if benchmark_results:
372
- self.console.print("\n[cyan]📊 Performance Benchmark Summary[/cyan]")
373
- self.console.print(f"Workflow Duration: [bold]{duration:.2f}s[/bold]")
374
-
375
- # Show key performance improvements if available
376
- for result in benchmark_results.results[:3]: # Top 3 results
377
- if result.time_improvement_percentage > 0:
378
- self.console.print(
379
- f"[green]⚡[/green] {result.test_name}: {result.time_improvement_percentage:.1f}% faster"
380
- )
381
-
382
- if result.cache_hit_ratio > 0:
383
- self.console.print(
384
- f"[blue]🎯[/blue] Cache efficiency: {result.cache_hit_ratio:.0%}"
385
- )
375
+ self._display_benchmark_results(benchmark_results, duration)
386
376
 
387
377
  except Exception as e:
388
378
  self.console.print(
@@ -392,6 +382,52 @@ class WorkflowPipeline:
392
382
  if self.debugger.enabled:
393
383
  self.debugger.print_debug_summary()
394
384
 
385
+ def _gather_performance_metrics(
386
+ self, workflow_id: str, duration: float, success: bool
387
+ ) -> dict:
388
+ """Gather performance metrics from workflow execution."""
389
+ return {
390
+ "workflow_id": workflow_id,
391
+ "total_duration": duration,
392
+ "success": success,
393
+ "cache_metrics": self._cache.get_stats() if self._cache else {},
394
+ "memory_metrics": self._memory_optimizer.get_stats()
395
+ if hasattr(self._memory_optimizer, "get_stats")
396
+ else {},
397
+ }
398
+
399
+ def _display_benchmark_results(
400
+ self, benchmark_results: t.Any, duration: float
401
+ ) -> None:
402
+ """Display compact performance summary."""
403
+ if not benchmark_results:
404
+ return
405
+
406
+ self.console.print("\n[cyan]📊 Performance Benchmark Summary[/cyan]")
407
+ self.console.print(f"Workflow Duration: [bold]{duration:.2f}s[/bold]")
408
+
409
+ self._show_performance_improvements(benchmark_results)
410
+
411
+ def _show_performance_improvements(self, benchmark_results: t.Any) -> None:
412
+ """Show key performance improvements from benchmark results."""
413
+ for result in benchmark_results.results[:3]: # Top 3 results
414
+ self._display_time_improvement(result)
415
+ self._display_cache_efficiency(result)
416
+
417
+ def _display_time_improvement(self, result: t.Any) -> None:
418
+ """Display time improvement percentage if available."""
419
+ if result.time_improvement_percentage > 0:
420
+ self.console.print(
421
+ f"[green]⚡[/green] {result.test_name}: {result.time_improvement_percentage:.1f}% faster"
422
+ )
423
+
424
+ def _display_cache_efficiency(self, result: t.Any) -> None:
425
+ """Display cache hit ratio if available."""
426
+ if result.cache_hit_ratio > 0:
427
+ self.console.print(
428
+ f"[blue]🎯[/blue] Cache efficiency: {result.cache_hit_ratio:.0%}"
429
+ )
430
+
395
431
  def _handle_user_interruption(self) -> bool:
396
432
  self.console.print("Interrupted by user")
397
433
  self.session.fail_task("workflow", "Interrupted by user")
@@ -418,11 +454,11 @@ class WorkflowPipeline:
418
454
  success = success and config_success
419
455
 
420
456
  quality_success = await self._execute_quality_phase(options, workflow_id)
421
- if not quality_success:
422
- success = False
457
+ success = success and quality_success
423
458
 
424
- if self._is_publishing_workflow(options):
425
- return False
459
+ # If quality phase failed and we're in publishing mode, stop here
460
+ if not quality_success and self._is_publishing_workflow(options):
461
+ return False
426
462
 
427
463
  # Execute publishing workflow if requested
428
464
  publishing_success = await self._execute_publishing_workflow(
@@ -447,6 +483,30 @@ class WorkflowPipeline:
447
483
 
448
484
  return success
449
485
 
486
+ def _handle_quality_phase_result(
487
+ self, success: bool, quality_success: bool, options: OptionsProtocol
488
+ ) -> bool:
489
+ """Handle the result of the quality phase execution."""
490
+ if not quality_success:
491
+ if self._is_publishing_workflow(options):
492
+ # For publishing workflows, quality failures should stop execution
493
+ return False
494
+ # For non-publishing workflows, we continue but mark as failed
495
+ return False
496
+ return success
497
+
498
+ def _handle_workflow_completion(
499
+ self, success: bool, publishing_success: bool, options: OptionsProtocol
500
+ ) -> bool:
501
+ """Handle workflow completion and determine final success status."""
502
+ # Only fail the overall workflow if publishing was explicitly requested and failed
503
+ if not publishing_success and (options.publish or options.all):
504
+ self.console.print(
505
+ "[red]❌ Publishing failed - overall workflow marked as failed[/red]"
506
+ )
507
+ return False
508
+ return success
509
+
450
510
  def _is_publishing_workflow(self, options: OptionsProtocol) -> bool:
451
511
  return bool(options.publish or options.all)
452
512
 
@@ -503,40 +563,52 @@ class WorkflowPipeline:
503
563
  if not self._quality_intelligence:
504
564
  return "Quality intelligence not available"
505
565
 
506
- # Analyze recent quality trends and anomalies
507
- anomalies = await self._quality_intelligence.detect_anomalies()
508
- patterns = await self._quality_intelligence.identify_patterns()
509
-
510
- # Make intelligent recommendations based on current state
511
- recommendations = []
512
- if anomalies:
513
- high_severity_anomalies = [
514
- a for a in anomalies if a.severity.name in ["CRITICAL", "HIGH"]
515
- ]
516
- if high_severity_anomalies:
517
- recommendations.append(
518
- "comprehensive analysis recommended due to quality anomalies"
519
- )
520
- else:
521
- recommendations.append("standard quality checks sufficient")
522
-
523
- if patterns:
524
- improving_patterns = [
525
- p for p in patterns if p.trend_direction.name == "IMPROVING"
526
- ]
527
- if improving_patterns:
528
- recommendations.append("quality trending upward")
529
- else:
530
- recommendations.append("quality monitoring active")
531
-
532
- if not recommendations:
533
- recommendations.append("baseline quality analysis active")
566
+ anomalies = self._quality_intelligence.detect_anomalies()
567
+ patterns = self._quality_intelligence.identify_patterns()
534
568
 
569
+ recommendations = self._build_quality_recommendations(anomalies, patterns)
535
570
  return "; ".join(recommendations)
536
571
 
537
572
  except Exception as e:
538
573
  return f"Quality intelligence analysis failed: {str(e)[:50]}..."
539
574
 
575
+ def _build_quality_recommendations(
576
+ self, anomalies: t.Any, patterns: t.Any
577
+ ) -> list[str]:
578
+ """Build quality recommendations based on anomalies and patterns."""
579
+ recommendations = []
580
+
581
+ if anomalies:
582
+ recommendations.extend(self._analyze_anomalies(anomalies))
583
+
584
+ if patterns:
585
+ recommendations.extend(self._analyze_patterns(patterns))
586
+
587
+ if not recommendations:
588
+ recommendations.append("baseline quality analysis active")
589
+
590
+ return recommendations
591
+
592
+ def _analyze_anomalies(self, anomalies: t.Any) -> list[str]:
593
+ """Analyze anomalies and return recommendations."""
594
+ high_severity_anomalies = [
595
+ a for a in anomalies if a.severity.name in ("CRITICAL", "HIGH")
596
+ ]
597
+
598
+ if high_severity_anomalies:
599
+ return ["comprehensive analysis recommended due to quality anomalies"]
600
+ return ["standard quality checks sufficient"]
601
+
602
+ def _analyze_patterns(self, patterns: t.Any) -> list[str]:
603
+ """Analyze patterns and return recommendations."""
604
+ improving_patterns = [
605
+ p for p in patterns if p.trend_direction.name == "IMPROVING"
606
+ ]
607
+
608
+ if improving_patterns:
609
+ return ["quality trending upward"]
610
+ return ["quality monitoring active"]
611
+
540
612
  async def _execute_test_workflow(
541
613
  self, options: OptionsProtocol, workflow_id: str
542
614
  ) -> bool:
@@ -864,27 +936,36 @@ class WorkflowPipeline:
864
936
  def _execute_standard_hooks_workflow(self, options: OptionsProtocol) -> bool:
865
937
  self._update_hooks_status_running()
866
938
 
867
- fast_hooks_success = self._run_fast_hooks_phase(options)
868
- if not fast_hooks_success:
939
+ if not self._execute_fast_hooks_workflow(options):
869
940
  self._handle_hooks_completion(False)
870
941
  return False
871
942
 
872
- if getattr(options, "clean", False):
873
- if not self._run_code_cleaning_phase(options):
874
- self._handle_hooks_completion(False)
875
- return False
876
-
877
- if not self._run_post_cleaning_fast_hooks(options):
878
- self._handle_hooks_completion(False)
879
- return False
880
- self._mark_code_cleaning_complete()
943
+ if not self._execute_cleaning_workflow_if_needed(options):
944
+ self._handle_hooks_completion(False)
945
+ return False
881
946
 
882
947
  comprehensive_success = self._run_comprehensive_hooks_phase(options)
948
+ self._handle_hooks_completion(comprehensive_success)
883
949
 
884
- hooks_success = fast_hooks_success and comprehensive_success
885
- self._handle_hooks_completion(hooks_success)
950
+ return comprehensive_success
886
951
 
887
- return hooks_success
952
+ def _execute_fast_hooks_workflow(self, options: OptionsProtocol) -> bool:
953
+ """Execute fast hooks phase."""
954
+ return self._run_fast_hooks_phase(options)
955
+
956
+ def _execute_cleaning_workflow_if_needed(self, options: OptionsProtocol) -> bool:
957
+ """Execute cleaning workflow if requested."""
958
+ if not getattr(options, "clean", False):
959
+ return True
960
+
961
+ if not self._run_code_cleaning_phase(options):
962
+ return False
963
+
964
+ if not self._run_post_cleaning_fast_hooks(options):
965
+ return False
966
+
967
+ self._mark_code_cleaning_complete()
968
+ return True
888
969
 
889
970
  def _update_hooks_status_running(self) -> None:
890
971
  if self._has_mcp_state_manager():
@@ -1094,15 +1175,16 @@ class WorkflowPipeline:
1094
1175
  return test_success
1095
1176
 
1096
1177
  def _should_verify_hook_fixes(self, fixes_applied: list[str]) -> bool:
1097
- hook_fixes = [
1098
- f
1099
- for f in fixes_applied
1100
- if "hook" not in f.lower()
1101
- or "complexity" in f.lower()
1102
- or "type" in f.lower()
1103
- ]
1178
+ hook_fixes = [fix for fix in fixes_applied if self._is_hook_related_fix(fix)]
1104
1179
  return bool(hook_fixes)
1105
1180
 
1181
+ def _is_hook_related_fix(self, fix: str) -> bool:
1182
+ """Check if a fix is related to hooks and should trigger hook verification."""
1183
+ fix_lower = fix.lower()
1184
+ return (
1185
+ "hook" not in fix_lower or "complexity" in fix_lower or "type" in fix_lower
1186
+ )
1187
+
1106
1188
  async def _verify_hook_fixes(self, options: OptionsProtocol) -> bool:
1107
1189
  self.logger.info("Re-running comprehensive hooks to verify hook fixes")
1108
1190
  hook_success = self.phases.run_comprehensive_hooks_only(options)
@@ -1277,35 +1359,30 @@ class WorkflowPipeline:
1277
1359
  return issues
1278
1360
 
1279
1361
  def _parse_comprehensive_hook_errors(self, error_msg: str) -> list[Issue]:
1280
- issues: list[Issue] = []
1281
1362
  error_lower = error_msg.lower()
1363
+ error_checkers = self._get_comprehensive_error_checkers()
1282
1364
 
1283
- complexity_issue = self._check_complexity_error(error_lower)
1284
- if complexity_issue:
1285
- issues.append(complexity_issue)
1286
-
1287
- type_error_issue = self._check_type_error(error_lower)
1288
- if type_error_issue:
1289
- issues.append(type_error_issue)
1290
-
1291
- security_issue = self._check_security_error(error_lower)
1292
- if security_issue:
1293
- issues.append(security_issue)
1294
-
1295
- performance_issue = self._check_performance_error(error_lower)
1296
- if performance_issue:
1297
- issues.append(performance_issue)
1298
-
1299
- dead_code_issue = self._check_dead_code_error(error_lower)
1300
- if dead_code_issue:
1301
- issues.append(dead_code_issue)
1302
-
1303
- regex_issue = self._check_regex_validation_error(error_lower)
1304
- if regex_issue:
1305
- issues.append(regex_issue)
1365
+ issues = []
1366
+ for check_func in error_checkers:
1367
+ issue = check_func(error_lower)
1368
+ if issue:
1369
+ issues.append(issue)
1306
1370
 
1307
1371
  return issues
1308
1372
 
1373
+ def _get_comprehensive_error_checkers(
1374
+ self,
1375
+ ) -> list[t.Callable[[str], Issue | None]]:
1376
+ """Get list of error checking functions for comprehensive hooks."""
1377
+ return [
1378
+ self._check_complexity_error,
1379
+ self._check_type_error,
1380
+ self._check_security_error,
1381
+ self._check_performance_error,
1382
+ self._check_dead_code_error,
1383
+ self._check_regex_validation_error,
1384
+ ]
1385
+
1309
1386
  def _check_complexity_error(self, error_lower: str) -> Issue | None:
1310
1387
  if "complexipy" in error_lower or "c901" in error_lower:
1311
1388
  return Issue(
@@ -1404,24 +1481,66 @@ class WorkflowPipeline:
1404
1481
  def _classify_issue(self, issue_str: str) -> tuple[IssueType, Priority]:
1405
1482
  issue_lower = issue_str.lower()
1406
1483
 
1407
- if self._is_type_error(issue_lower):
1408
- return IssueType.TYPE_ERROR, Priority.HIGH
1409
- if self._is_security_issue(issue_lower):
1410
- return IssueType.SECURITY, Priority.HIGH
1411
- if self._is_complexity_issue(issue_lower):
1412
- return IssueType.COMPLEXITY, Priority.HIGH
1413
- if self._is_regex_validation_issue(issue_lower):
1414
- return IssueType.REGEX_VALIDATION, Priority.HIGH
1415
-
1416
- if self._is_dead_code_issue(issue_lower):
1417
- return IssueType.DEAD_CODE, Priority.MEDIUM
1418
- if self._is_performance_issue(issue_lower):
1419
- return IssueType.PERFORMANCE, Priority.MEDIUM
1420
- if self._is_import_error(issue_lower):
1421
- return IssueType.IMPORT_ERROR, Priority.MEDIUM
1484
+ # Check high priority issues first
1485
+ high_priority_result = self._check_high_priority_issues(issue_lower)
1486
+ if high_priority_result:
1487
+ return high_priority_result
1488
+
1489
+ # Check medium priority issues
1490
+ medium_priority_result = self._check_medium_priority_issues(issue_lower)
1491
+ if medium_priority_result:
1492
+ return medium_priority_result
1422
1493
 
1494
+ # Default to formatting issue
1423
1495
  return IssueType.FORMATTING, Priority.MEDIUM
1424
1496
 
1497
+ def _check_high_priority_issues(
1498
+ self, issue_lower: str
1499
+ ) -> tuple[IssueType, Priority] | None:
1500
+ """Check for high priority issue types.
1501
+
1502
+ Args:
1503
+ issue_lower: Lowercase issue string
1504
+
1505
+ Returns:
1506
+ Tuple of issue type and priority if found, None otherwise
1507
+ """
1508
+ high_priority_checks = [
1509
+ (self._is_type_error, IssueType.TYPE_ERROR),
1510
+ (self._is_security_issue, IssueType.SECURITY),
1511
+ (self._is_complexity_issue, IssueType.COMPLEXITY),
1512
+ (self._is_regex_validation_issue, IssueType.REGEX_VALIDATION),
1513
+ ]
1514
+
1515
+ for check_func, issue_type in high_priority_checks:
1516
+ if check_func(issue_lower):
1517
+ return issue_type, Priority.HIGH
1518
+
1519
+ return None
1520
+
1521
+ def _check_medium_priority_issues(
1522
+ self, issue_lower: str
1523
+ ) -> tuple[IssueType, Priority] | None:
1524
+ """Check for medium priority issue types.
1525
+
1526
+ Args:
1527
+ issue_lower: Lowercase issue string
1528
+
1529
+ Returns:
1530
+ Tuple of issue type and priority if found, None otherwise
1531
+ """
1532
+ medium_priority_checks = [
1533
+ (self._is_dead_code_issue, IssueType.DEAD_CODE),
1534
+ (self._is_performance_issue, IssueType.PERFORMANCE),
1535
+ (self._is_import_error, IssueType.IMPORT_ERROR),
1536
+ ]
1537
+
1538
+ for check_func, issue_type in medium_priority_checks:
1539
+ if check_func(issue_lower):
1540
+ return issue_type, Priority.MEDIUM
1541
+
1542
+ return None
1543
+
1425
1544
  def _is_type_error(self, issue_lower: str) -> bool:
1426
1545
  return any(
1427
1546
  keyword in issue_lower for keyword in ("type", "annotation", "pyright")
@@ -1490,44 +1609,77 @@ class WorkflowPipeline:
1490
1609
  async def _handle_security_gate_failure(
1491
1610
  self, options: OptionsProtocol, allow_ai_fixing: bool = False
1492
1611
  ) -> bool:
1612
+ self._display_security_gate_failure_message()
1613
+
1614
+ if allow_ai_fixing:
1615
+ return await self._attempt_ai_assisted_security_fix(options)
1616
+ return self._handle_manual_security_fix()
1617
+
1618
+ def _display_security_gate_failure_message(self) -> None:
1619
+ """Display initial security gate failure message."""
1493
1620
  self.console.print(
1494
1621
  "[red]🔒 SECURITY GATE: Critical security checks failed[/red]"
1495
1622
  )
1496
1623
 
1497
- if allow_ai_fixing:
1498
- self.console.print(
1499
- "[red]Security-critical hooks (bandit, pyright, gitleaks) must pass before publishing[/red]"
1500
- )
1501
- self.console.print(
1502
- "[yellow]🤖 Attempting AI-assisted security issue resolution...[/yellow]"
1503
- )
1624
+ async def _attempt_ai_assisted_security_fix(self, options: OptionsProtocol) -> bool:
1625
+ """Attempt to fix security issues using AI assistance.
1504
1626
 
1505
- ai_fix_success = await self._run_ai_agent_fixing_phase(options)
1506
- if ai_fix_success:
1507
- try:
1508
- security_still_blocks = self._check_security_critical_failures()
1509
- if not security_still_blocks:
1510
- self.console.print(
1511
- "[green]✅ AI agents resolved security issues - publishing allowed[/green]"
1512
- )
1513
- return True
1514
- else:
1515
- self.console.print(
1516
- "[red]🔒 Security issues persist after AI fixing - publishing still BLOCKED[/red]"
1517
- )
1518
- return False
1519
- except Exception as e:
1520
- self.logger.warning(
1521
- f"Security re-check failed: {e} - blocking publishing"
1522
- )
1523
- return False
1524
- return False
1525
- else:
1526
- self.console.print(
1527
- "[red]Security-critical hooks (bandit, pyright, gitleaks) must pass before publishing[/red]"
1528
- )
1627
+ Args:
1628
+ options: Configuration options
1629
+
1630
+ Returns:
1631
+ True if security issues were resolved, False otherwise
1632
+ """
1633
+ self._display_ai_fixing_messages()
1634
+
1635
+ ai_fix_success = await self._run_ai_agent_fixing_phase(options)
1636
+ if ai_fix_success:
1637
+ return self._verify_security_fix_success()
1638
+
1639
+ return False
1640
+
1641
+ def _display_ai_fixing_messages(self) -> None:
1642
+ """Display messages about AI-assisted security fixing."""
1643
+ self.console.print(
1644
+ "[red]Security-critical hooks (bandit, pyright, gitleaks) must pass before publishing[/red]"
1645
+ )
1646
+ self.console.print(
1647
+ "[yellow]🤖 Attempting AI-assisted security issue resolution...[/yellow]"
1648
+ )
1649
+
1650
+ def _verify_security_fix_success(self) -> bool:
1651
+ """Verify that AI fixes resolved the security issues.
1652
+
1653
+ Returns:
1654
+ True if security issues were resolved, False otherwise
1655
+ """
1656
+ try:
1657
+ security_still_blocks = self._check_security_critical_failures()
1658
+ if not security_still_blocks:
1659
+ self.console.print(
1660
+ "[green]✅ AI agents resolved security issues - publishing allowed[/green]"
1661
+ )
1662
+ return True
1663
+ else:
1664
+ self.console.print(
1665
+ "[red]🔒 Security issues persist after AI fixing - publishing still BLOCKED[/red]"
1666
+ )
1667
+ return False
1668
+ except Exception as e:
1669
+ self.logger.warning(f"Security re-check failed: {e} - blocking publishing")
1529
1670
  return False
1530
1671
 
1672
+ def _handle_manual_security_fix(self) -> bool:
1673
+ """Handle security fix when AI assistance is not allowed.
1674
+
1675
+ Returns:
1676
+ Always False since manual intervention is required
1677
+ """
1678
+ self.console.print(
1679
+ "[red]Security-critical hooks (bandit, pyright, gitleaks) must pass before publishing[/red]"
1680
+ )
1681
+ return False
1682
+
1531
1683
  def _determine_ai_fixing_needed(
1532
1684
  self,
1533
1685
  testing_passed: bool,