crackerjack 0.30.3__py3-none-any.whl → 0.31.4__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/CLAUDE.md +1005 -0
- crackerjack/RULES.md +380 -0
- crackerjack/__init__.py +42 -13
- crackerjack/__main__.py +225 -299
- crackerjack/agents/__init__.py +41 -0
- crackerjack/agents/architect_agent.py +281 -0
- crackerjack/agents/base.py +169 -0
- crackerjack/agents/coordinator.py +512 -0
- crackerjack/agents/documentation_agent.py +498 -0
- crackerjack/agents/dry_agent.py +388 -0
- crackerjack/agents/formatting_agent.py +245 -0
- crackerjack/agents/import_optimization_agent.py +281 -0
- crackerjack/agents/performance_agent.py +669 -0
- crackerjack/agents/proactive_agent.py +104 -0
- crackerjack/agents/refactoring_agent.py +788 -0
- crackerjack/agents/security_agent.py +529 -0
- crackerjack/agents/test_creation_agent.py +652 -0
- crackerjack/agents/test_specialist_agent.py +486 -0
- crackerjack/agents/tracker.py +212 -0
- crackerjack/api.py +560 -0
- crackerjack/cli/__init__.py +24 -0
- crackerjack/cli/facade.py +104 -0
- crackerjack/cli/handlers.py +267 -0
- crackerjack/cli/interactive.py +471 -0
- crackerjack/cli/options.py +401 -0
- crackerjack/cli/utils.py +18 -0
- crackerjack/code_cleaner.py +618 -928
- crackerjack/config/__init__.py +19 -0
- crackerjack/config/hooks.py +218 -0
- crackerjack/core/__init__.py +0 -0
- crackerjack/core/async_workflow_orchestrator.py +406 -0
- crackerjack/core/autofix_coordinator.py +200 -0
- crackerjack/core/container.py +104 -0
- crackerjack/core/enhanced_container.py +542 -0
- crackerjack/core/performance.py +243 -0
- crackerjack/core/phase_coordinator.py +561 -0
- crackerjack/core/proactive_workflow.py +316 -0
- crackerjack/core/session_coordinator.py +289 -0
- crackerjack/core/workflow_orchestrator.py +640 -0
- crackerjack/dynamic_config.py +94 -103
- crackerjack/errors.py +263 -41
- crackerjack/executors/__init__.py +11 -0
- crackerjack/executors/async_hook_executor.py +431 -0
- crackerjack/executors/cached_hook_executor.py +242 -0
- crackerjack/executors/hook_executor.py +345 -0
- crackerjack/executors/individual_hook_executor.py +669 -0
- crackerjack/intelligence/__init__.py +44 -0
- crackerjack/intelligence/adaptive_learning.py +751 -0
- crackerjack/intelligence/agent_orchestrator.py +551 -0
- crackerjack/intelligence/agent_registry.py +414 -0
- crackerjack/intelligence/agent_selector.py +502 -0
- crackerjack/intelligence/integration.py +290 -0
- crackerjack/interactive.py +576 -315
- crackerjack/managers/__init__.py +11 -0
- crackerjack/managers/async_hook_manager.py +135 -0
- crackerjack/managers/hook_manager.py +137 -0
- crackerjack/managers/publish_manager.py +411 -0
- crackerjack/managers/test_command_builder.py +151 -0
- crackerjack/managers/test_executor.py +435 -0
- crackerjack/managers/test_manager.py +258 -0
- crackerjack/managers/test_manager_backup.py +1124 -0
- crackerjack/managers/test_progress.py +144 -0
- crackerjack/mcp/__init__.py +0 -0
- crackerjack/mcp/cache.py +336 -0
- crackerjack/mcp/client_runner.py +104 -0
- crackerjack/mcp/context.py +615 -0
- crackerjack/mcp/dashboard.py +636 -0
- crackerjack/mcp/enhanced_progress_monitor.py +479 -0
- crackerjack/mcp/file_monitor.py +336 -0
- crackerjack/mcp/progress_components.py +569 -0
- crackerjack/mcp/progress_monitor.py +949 -0
- crackerjack/mcp/rate_limiter.py +332 -0
- crackerjack/mcp/server.py +22 -0
- crackerjack/mcp/server_core.py +244 -0
- crackerjack/mcp/service_watchdog.py +501 -0
- crackerjack/mcp/state.py +395 -0
- crackerjack/mcp/task_manager.py +257 -0
- crackerjack/mcp/tools/__init__.py +17 -0
- crackerjack/mcp/tools/core_tools.py +249 -0
- crackerjack/mcp/tools/error_analyzer.py +308 -0
- crackerjack/mcp/tools/execution_tools.py +370 -0
- crackerjack/mcp/tools/execution_tools_backup.py +1097 -0
- crackerjack/mcp/tools/intelligence_tool_registry.py +80 -0
- crackerjack/mcp/tools/intelligence_tools.py +314 -0
- crackerjack/mcp/tools/monitoring_tools.py +502 -0
- crackerjack/mcp/tools/proactive_tools.py +384 -0
- crackerjack/mcp/tools/progress_tools.py +141 -0
- crackerjack/mcp/tools/utility_tools.py +341 -0
- crackerjack/mcp/tools/workflow_executor.py +360 -0
- crackerjack/mcp/websocket/__init__.py +14 -0
- crackerjack/mcp/websocket/app.py +39 -0
- crackerjack/mcp/websocket/endpoints.py +559 -0
- crackerjack/mcp/websocket/jobs.py +253 -0
- crackerjack/mcp/websocket/server.py +116 -0
- crackerjack/mcp/websocket/websocket_handler.py +78 -0
- crackerjack/mcp/websocket_server.py +10 -0
- crackerjack/models/__init__.py +31 -0
- crackerjack/models/config.py +93 -0
- crackerjack/models/config_adapter.py +230 -0
- crackerjack/models/protocols.py +118 -0
- crackerjack/models/task.py +154 -0
- crackerjack/monitoring/ai_agent_watchdog.py +450 -0
- crackerjack/monitoring/regression_prevention.py +638 -0
- crackerjack/orchestration/__init__.py +0 -0
- crackerjack/orchestration/advanced_orchestrator.py +970 -0
- crackerjack/orchestration/execution_strategies.py +341 -0
- crackerjack/orchestration/test_progress_streamer.py +636 -0
- crackerjack/plugins/__init__.py +15 -0
- crackerjack/plugins/base.py +200 -0
- crackerjack/plugins/hooks.py +246 -0
- crackerjack/plugins/loader.py +335 -0
- crackerjack/plugins/managers.py +259 -0
- crackerjack/py313.py +8 -3
- crackerjack/services/__init__.py +22 -0
- crackerjack/services/cache.py +314 -0
- crackerjack/services/config.py +347 -0
- crackerjack/services/config_integrity.py +99 -0
- crackerjack/services/contextual_ai_assistant.py +516 -0
- crackerjack/services/coverage_ratchet.py +347 -0
- crackerjack/services/debug.py +736 -0
- crackerjack/services/dependency_monitor.py +617 -0
- crackerjack/services/enhanced_filesystem.py +439 -0
- crackerjack/services/file_hasher.py +151 -0
- crackerjack/services/filesystem.py +395 -0
- crackerjack/services/git.py +165 -0
- crackerjack/services/health_metrics.py +611 -0
- crackerjack/services/initialization.py +847 -0
- crackerjack/services/log_manager.py +286 -0
- crackerjack/services/logging.py +174 -0
- crackerjack/services/metrics.py +578 -0
- crackerjack/services/pattern_cache.py +362 -0
- crackerjack/services/pattern_detector.py +515 -0
- crackerjack/services/performance_benchmarks.py +653 -0
- crackerjack/services/security.py +163 -0
- crackerjack/services/server_manager.py +234 -0
- crackerjack/services/smart_scheduling.py +144 -0
- crackerjack/services/tool_version_service.py +61 -0
- crackerjack/services/unified_config.py +437 -0
- crackerjack/services/version_checker.py +248 -0
- crackerjack/slash_commands/__init__.py +14 -0
- crackerjack/slash_commands/init.md +122 -0
- crackerjack/slash_commands/run.md +163 -0
- crackerjack/slash_commands/status.md +127 -0
- crackerjack-0.31.4.dist-info/METADATA +742 -0
- crackerjack-0.31.4.dist-info/RECORD +148 -0
- crackerjack-0.31.4.dist-info/entry_points.txt +2 -0
- crackerjack/.gitignore +0 -34
- crackerjack/.libcst.codemod.yaml +0 -18
- crackerjack/.pdm.toml +0 -1
- crackerjack/crackerjack.py +0 -3805
- crackerjack/pyproject.toml +0 -286
- crackerjack-0.30.3.dist-info/METADATA +0 -1290
- crackerjack-0.30.3.dist-info/RECORD +0 -16
- {crackerjack-0.30.3.dist-info → crackerjack-0.31.4.dist-info}/WHEEL +0 -0
- {crackerjack-0.30.3.dist-info → crackerjack-0.31.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import time
|
|
4
|
+
import typing as t
|
|
5
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
11
|
+
from crackerjack.config.hooks import HookDefinition, HookStrategy, RetryPolicy
|
|
12
|
+
from crackerjack.models.task import HookResult
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class HookExecutionResult:
|
|
17
|
+
strategy_name: str
|
|
18
|
+
results: list[HookResult]
|
|
19
|
+
total_duration: float
|
|
20
|
+
success: bool
|
|
21
|
+
concurrent_execution: bool = False
|
|
22
|
+
cache_hits: int = 0
|
|
23
|
+
cache_misses: int = 0
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def failed_count(self) -> int:
|
|
27
|
+
return sum(1 for r in self.results if r.status == "failed")
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def passed_count(self) -> int:
|
|
31
|
+
return sum(1 for r in self.results if r.status == "passed")
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def cache_hit_rate(self) -> float:
|
|
35
|
+
total_requests = self.cache_hits + self.cache_misses
|
|
36
|
+
return (self.cache_hits / total_requests * 100) if total_requests > 0 else 0.0
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def performance_summary(self) -> dict[str, t.Any]:
|
|
40
|
+
return {
|
|
41
|
+
"total_hooks": len(self.results),
|
|
42
|
+
"passed": self.passed_count,
|
|
43
|
+
"failed": self.failed_count,
|
|
44
|
+
"duration_seconds": round(self.total_duration, 2),
|
|
45
|
+
"concurrent": self.concurrent_execution,
|
|
46
|
+
"cache_hits": self.cache_hits,
|
|
47
|
+
"cache_misses": self.cache_misses,
|
|
48
|
+
"cache_hit_rate_percent": round(self.cache_hit_rate, 1),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class HookExecutor:
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
console: Console,
|
|
56
|
+
pkg_path: Path,
|
|
57
|
+
verbose: bool = False,
|
|
58
|
+
quiet: bool = False,
|
|
59
|
+
) -> None:
|
|
60
|
+
self.console = console
|
|
61
|
+
self.pkg_path = pkg_path
|
|
62
|
+
self.verbose = verbose
|
|
63
|
+
self.quiet = quiet
|
|
64
|
+
|
|
65
|
+
def execute_strategy(self, strategy: HookStrategy) -> HookExecutionResult:
|
|
66
|
+
start_time = time.time()
|
|
67
|
+
|
|
68
|
+
self._print_strategy_header(strategy)
|
|
69
|
+
|
|
70
|
+
if strategy.parallel and len(strategy.hooks) > 1:
|
|
71
|
+
results = self._execute_parallel(strategy)
|
|
72
|
+
else:
|
|
73
|
+
results = self._execute_sequential(strategy)
|
|
74
|
+
|
|
75
|
+
if strategy.retry_policy != RetryPolicy.NONE:
|
|
76
|
+
results = self._handle_retries(strategy, results)
|
|
77
|
+
|
|
78
|
+
total_duration = time.time() - start_time
|
|
79
|
+
success = all(r.status == "passed" for r in results)
|
|
80
|
+
|
|
81
|
+
if not self.quiet:
|
|
82
|
+
self._print_summary(strategy, results, success)
|
|
83
|
+
|
|
84
|
+
return HookExecutionResult(
|
|
85
|
+
strategy_name=strategy.name,
|
|
86
|
+
results=results,
|
|
87
|
+
total_duration=total_duration,
|
|
88
|
+
success=success,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def _print_strategy_header(self, strategy: HookStrategy) -> None:
|
|
92
|
+
self.console.print("\n" + "-" * 80)
|
|
93
|
+
if strategy.name == "fast":
|
|
94
|
+
self.console.print(
|
|
95
|
+
"[bold bright_cyan]🔍 HOOKS[/bold bright_cyan] [bold bright_white]Running code quality checks[/bold bright_white]",
|
|
96
|
+
)
|
|
97
|
+
elif strategy.name == "comprehensive":
|
|
98
|
+
self.console.print(
|
|
99
|
+
"[bold bright_cyan]🔍 HOOKS[/bold bright_cyan] [bold bright_white]Running comprehensive quality checks[/bold bright_white]",
|
|
100
|
+
)
|
|
101
|
+
else:
|
|
102
|
+
self.console.print(
|
|
103
|
+
f"[bold bright_cyan]🔍 HOOKS[/bold bright_cyan] [bold bright_white]Running {strategy.name} hooks[/bold bright_white]",
|
|
104
|
+
)
|
|
105
|
+
self.console.print("-" * 80 + "\n")
|
|
106
|
+
|
|
107
|
+
def _execute_sequential(self, strategy: HookStrategy) -> list[HookResult]:
|
|
108
|
+
results: list[HookResult] = []
|
|
109
|
+
for hook in strategy.hooks:
|
|
110
|
+
result = self.execute_single_hook(hook)
|
|
111
|
+
results.append(result)
|
|
112
|
+
self._display_hook_result(result)
|
|
113
|
+
return results
|
|
114
|
+
|
|
115
|
+
def _execute_parallel(self, strategy: HookStrategy) -> list[HookResult]:
|
|
116
|
+
results: list[HookResult] = []
|
|
117
|
+
|
|
118
|
+
formatting_hooks = [h for h in strategy.hooks if h.is_formatting]
|
|
119
|
+
other_hooks = [h for h in strategy.hooks if not h.is_formatting]
|
|
120
|
+
|
|
121
|
+
for hook in formatting_hooks:
|
|
122
|
+
result = self.execute_single_hook(hook)
|
|
123
|
+
results.append(result)
|
|
124
|
+
self._display_hook_result(result)
|
|
125
|
+
|
|
126
|
+
if other_hooks:
|
|
127
|
+
with ThreadPoolExecutor(max_workers=strategy.max_workers) as executor:
|
|
128
|
+
future_to_hook = {
|
|
129
|
+
executor.submit(self.execute_single_hook, hook): hook
|
|
130
|
+
for hook in other_hooks
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
for future in as_completed(future_to_hook):
|
|
134
|
+
try:
|
|
135
|
+
result = future.result()
|
|
136
|
+
results.append(result)
|
|
137
|
+
self._display_hook_result(result)
|
|
138
|
+
except Exception as e:
|
|
139
|
+
hook = future_to_hook[future]
|
|
140
|
+
error_result = HookResult(
|
|
141
|
+
id=hook.name,
|
|
142
|
+
name=hook.name,
|
|
143
|
+
status="error",
|
|
144
|
+
duration=0.0,
|
|
145
|
+
issues_found=[str(e)],
|
|
146
|
+
stage=hook.stage.value,
|
|
147
|
+
)
|
|
148
|
+
results.append(error_result)
|
|
149
|
+
self._display_hook_result(error_result)
|
|
150
|
+
|
|
151
|
+
return results
|
|
152
|
+
|
|
153
|
+
def execute_single_hook(self, hook: HookDefinition) -> HookResult:
|
|
154
|
+
start_time = time.time()
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
clean_env = self._get_clean_environment()
|
|
158
|
+
result = subprocess.run(
|
|
159
|
+
hook.get_command(),
|
|
160
|
+
check=False,
|
|
161
|
+
cwd=self.pkg_path,
|
|
162
|
+
text=True,
|
|
163
|
+
timeout=hook.timeout,
|
|
164
|
+
env=clean_env,
|
|
165
|
+
capture_output=True,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
duration = time.time() - start_time
|
|
169
|
+
|
|
170
|
+
# Only display raw output in verbose mode, and only for failed hooks
|
|
171
|
+
if result.returncode != 0 and self.verbose:
|
|
172
|
+
if result.stdout:
|
|
173
|
+
self.console.print(result.stdout)
|
|
174
|
+
if result.stderr:
|
|
175
|
+
self.console.print(result.stderr)
|
|
176
|
+
|
|
177
|
+
return HookResult(
|
|
178
|
+
id=hook.name,
|
|
179
|
+
name=hook.name,
|
|
180
|
+
status="passed" if result.returncode == 0 else "failed",
|
|
181
|
+
duration=duration,
|
|
182
|
+
files_processed=0,
|
|
183
|
+
issues_found=[]
|
|
184
|
+
if result.returncode == 0
|
|
185
|
+
else [f"Hook failed with code {result.returncode}"],
|
|
186
|
+
stage=hook.stage.value,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
except subprocess.TimeoutExpired:
|
|
190
|
+
duration = time.time() - start_time
|
|
191
|
+
return HookResult(
|
|
192
|
+
id=hook.name,
|
|
193
|
+
name=hook.name,
|
|
194
|
+
status="timeout",
|
|
195
|
+
duration=duration,
|
|
196
|
+
issues_found=[f"Hook timed out after {duration:.1f}s"],
|
|
197
|
+
stage=hook.stage.value,
|
|
198
|
+
)
|
|
199
|
+
except Exception as e:
|
|
200
|
+
duration = time.time() - start_time
|
|
201
|
+
return HookResult(
|
|
202
|
+
id=hook.name,
|
|
203
|
+
name=hook.name,
|
|
204
|
+
status="error",
|
|
205
|
+
duration=duration,
|
|
206
|
+
issues_found=[str(e)],
|
|
207
|
+
stage=hook.stage.value,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
def _parse_hook_output(
|
|
211
|
+
self,
|
|
212
|
+
result: subprocess.CompletedProcess[str],
|
|
213
|
+
) -> dict[str, t.Any]:
|
|
214
|
+
output = result.stdout + result.stderr
|
|
215
|
+
return {
|
|
216
|
+
"hook_id": None,
|
|
217
|
+
"exit_code": result.returncode,
|
|
218
|
+
"files_processed": 0,
|
|
219
|
+
"issues": [],
|
|
220
|
+
"raw_output": output,
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
def _display_hook_result(self, result: HookResult) -> None:
|
|
224
|
+
status_icon = "✅" if result.status == "passed" else "❌"
|
|
225
|
+
|
|
226
|
+
# Create dot-filled line like classic pre-commit format
|
|
227
|
+
max_width = 70 # Leave room for terminal margins
|
|
228
|
+
|
|
229
|
+
if len(result.name) > max_width:
|
|
230
|
+
# Truncate long names
|
|
231
|
+
line = result.name[: max_width - 3] + "..."
|
|
232
|
+
else:
|
|
233
|
+
# Fill with dots to reach max width
|
|
234
|
+
dots_needed = max_width - len(result.name)
|
|
235
|
+
line = result.name + ("." * dots_needed)
|
|
236
|
+
|
|
237
|
+
self.console.print(f"{line} {status_icon}")
|
|
238
|
+
|
|
239
|
+
def _handle_retries(
|
|
240
|
+
self,
|
|
241
|
+
strategy: HookStrategy,
|
|
242
|
+
results: list[HookResult],
|
|
243
|
+
) -> list[HookResult]:
|
|
244
|
+
if strategy.retry_policy == RetryPolicy.FORMATTING_ONLY:
|
|
245
|
+
return self._retry_formatting_hooks(strategy, results)
|
|
246
|
+
if strategy.retry_policy == RetryPolicy.ALL_HOOKS:
|
|
247
|
+
return self._retry_all_hooks(strategy, results)
|
|
248
|
+
return results
|
|
249
|
+
|
|
250
|
+
def _retry_formatting_hooks(
|
|
251
|
+
self,
|
|
252
|
+
strategy: HookStrategy,
|
|
253
|
+
results: list[HookResult],
|
|
254
|
+
) -> list[HookResult]:
|
|
255
|
+
formatting_hooks_failed: set[str] = set()
|
|
256
|
+
|
|
257
|
+
for i, result in enumerate(results):
|
|
258
|
+
hook = strategy.hooks[i]
|
|
259
|
+
if hook.is_formatting and result.status == "failed":
|
|
260
|
+
formatting_hooks_failed.add(hook.name)
|
|
261
|
+
|
|
262
|
+
if not formatting_hooks_failed:
|
|
263
|
+
return results
|
|
264
|
+
|
|
265
|
+
updated_results: list[HookResult] = []
|
|
266
|
+
for i, hook in enumerate(strategy.hooks):
|
|
267
|
+
prev_result = results[i]
|
|
268
|
+
new_result = self.execute_single_hook(hook)
|
|
269
|
+
|
|
270
|
+
new_result.duration += prev_result.duration
|
|
271
|
+
updated_results.append(new_result)
|
|
272
|
+
self._display_hook_result(new_result)
|
|
273
|
+
|
|
274
|
+
return updated_results
|
|
275
|
+
|
|
276
|
+
def _retry_all_hooks(
|
|
277
|
+
self,
|
|
278
|
+
strategy: HookStrategy,
|
|
279
|
+
results: list[HookResult],
|
|
280
|
+
) -> list[HookResult]:
|
|
281
|
+
failed_hooks = [i for i, r in enumerate(results) if r.status == "failed"]
|
|
282
|
+
|
|
283
|
+
if not failed_hooks:
|
|
284
|
+
return results
|
|
285
|
+
|
|
286
|
+
updated_results: list[HookResult] = results.copy()
|
|
287
|
+
for i in failed_hooks:
|
|
288
|
+
hook = strategy.hooks[i]
|
|
289
|
+
prev_result = results[i]
|
|
290
|
+
new_result = self.execute_single_hook(hook)
|
|
291
|
+
|
|
292
|
+
new_result.duration += prev_result.duration
|
|
293
|
+
updated_results[i] = new_result
|
|
294
|
+
self._display_hook_result(new_result)
|
|
295
|
+
|
|
296
|
+
return updated_results
|
|
297
|
+
|
|
298
|
+
def _get_clean_environment(self) -> dict[str, str]:
|
|
299
|
+
clean_env = {
|
|
300
|
+
"HOME": os.environ.get("HOME", ""),
|
|
301
|
+
"USER": os.environ.get("USER", ""),
|
|
302
|
+
"SHELL": os.environ.get("SHELL", "/bin/bash"),
|
|
303
|
+
"LANG": os.environ.get("LANG", "en_US.UTF-8"),
|
|
304
|
+
"LC_ALL": os.environ.get("LC_ALL", ""),
|
|
305
|
+
"TERM": os.environ.get("TERM", "xterm-256color"),
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
system_path = os.environ.get("PATH", "")
|
|
309
|
+
if system_path:
|
|
310
|
+
venv_bin = str(Path(self.pkg_path) / ".venv" / "bin")
|
|
311
|
+
path_parts = [p for p in system_path.split(":") if p != venv_bin]
|
|
312
|
+
clean_env["PATH"] = ":".join(path_parts)
|
|
313
|
+
|
|
314
|
+
python_vars_to_exclude = {
|
|
315
|
+
"VIRTUAL_ENV",
|
|
316
|
+
"PYTHONPATH",
|
|
317
|
+
"PYTHON_PATH",
|
|
318
|
+
"PIP_CONFIG_FILE",
|
|
319
|
+
"PYTHONHOME",
|
|
320
|
+
"CONDA_DEFAULT_ENV",
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
for key, value in os.environ.items():
|
|
324
|
+
if key not in python_vars_to_exclude and key not in clean_env:
|
|
325
|
+
if not key.startswith(("PYTHON", "PIP_", "CONDA_", "VIRTUAL_")):
|
|
326
|
+
clean_env[key] = value
|
|
327
|
+
|
|
328
|
+
return clean_env
|
|
329
|
+
|
|
330
|
+
def _print_summary(
|
|
331
|
+
self,
|
|
332
|
+
strategy: HookStrategy,
|
|
333
|
+
results: list[HookResult],
|
|
334
|
+
success: bool,
|
|
335
|
+
) -> None:
|
|
336
|
+
if success:
|
|
337
|
+
self.console.print(
|
|
338
|
+
f"[green]✅[/green] {strategy.name.title()} hooks passed: {len(results)} / {len(results)}",
|
|
339
|
+
)
|
|
340
|
+
else:
|
|
341
|
+
failed_count = sum(1 for r in results if r.status == "failed")
|
|
342
|
+
error_count = sum(1 for r in results if r.status in ("timeout", "error"))
|
|
343
|
+
self.console.print(
|
|
344
|
+
f"[red]❌[/red] {strategy.name.title()} hooks failed: {failed_count} failed, {error_count} errors",
|
|
345
|
+
)
|