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,243 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import time
|
|
3
|
+
import typing as t
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FileCache:
|
|
10
|
+
def __init__(self, ttl: float = 300.0) -> None:
|
|
11
|
+
self.ttl = ttl
|
|
12
|
+
self._cache: dict[str, tuple[float, t.Any]] = {}
|
|
13
|
+
|
|
14
|
+
def get(self, key: str) -> t.Any | None:
|
|
15
|
+
if key not in self._cache:
|
|
16
|
+
return None
|
|
17
|
+
timestamp, value = self._cache[key]
|
|
18
|
+
if time.time() - timestamp > self.ttl:
|
|
19
|
+
del self._cache[key]
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
return value
|
|
23
|
+
|
|
24
|
+
def set(self, key: str, value: t.Any) -> None:
|
|
25
|
+
self._cache[key] = (time.time(), value)
|
|
26
|
+
|
|
27
|
+
def clear(self) -> None:
|
|
28
|
+
self._cache.clear()
|
|
29
|
+
|
|
30
|
+
def size(self) -> int:
|
|
31
|
+
return len(self._cache)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PerformanceMonitor:
|
|
35
|
+
def __init__(self, console: Console | None = None) -> None:
|
|
36
|
+
self.console = console
|
|
37
|
+
self.metrics: dict[str, list[float]] = {}
|
|
38
|
+
|
|
39
|
+
def time_operation(
|
|
40
|
+
self,
|
|
41
|
+
operation_name: str,
|
|
42
|
+
) -> t.Callable[[t.Callable[..., t.Any]], t.Callable[..., t.Any]]:
|
|
43
|
+
def decorator(func: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]:
|
|
44
|
+
@functools.wraps(func)
|
|
45
|
+
def wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any:
|
|
46
|
+
start_time = time.time()
|
|
47
|
+
try:
|
|
48
|
+
return func(*args, **kwargs)
|
|
49
|
+
finally:
|
|
50
|
+
duration = time.time() - start_time
|
|
51
|
+
self.record_metric(operation_name, duration)
|
|
52
|
+
|
|
53
|
+
return wrapper
|
|
54
|
+
|
|
55
|
+
return decorator
|
|
56
|
+
|
|
57
|
+
def record_metric(self, name: str, value: float) -> None:
|
|
58
|
+
if name not in self.metrics:
|
|
59
|
+
self.metrics[name] = []
|
|
60
|
+
self.metrics[name].append(value)
|
|
61
|
+
|
|
62
|
+
def get_stats(self, name: str) -> dict[str, float]:
|
|
63
|
+
if name not in self.metrics or not self.metrics[name]:
|
|
64
|
+
return {}
|
|
65
|
+
values = self.metrics[name]
|
|
66
|
+
return {
|
|
67
|
+
"count": len(values),
|
|
68
|
+
"total": sum(values),
|
|
69
|
+
"avg": sum(values) / len(values),
|
|
70
|
+
"min": min(values),
|
|
71
|
+
"max": max(values),
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
def print_stats(self, name: str | None = None) -> None:
|
|
75
|
+
if not self.console:
|
|
76
|
+
return
|
|
77
|
+
if name:
|
|
78
|
+
stats = self.get_stats(name)
|
|
79
|
+
if stats:
|
|
80
|
+
self.console.print(
|
|
81
|
+
f"[cyan]📊 {name}: [/cyan] "
|
|
82
|
+
f"avg = {stats['avg']:.3f}s, "
|
|
83
|
+
f"min = {stats['min']:.3f}s, "
|
|
84
|
+
f"max = {stats['max']:.3f}s, "
|
|
85
|
+
f"count = {stats['count']}",
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
for metric_name in self.metrics:
|
|
89
|
+
self.print_stats(metric_name)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def memoize_with_ttl(
|
|
93
|
+
ttl: float = 300.0,
|
|
94
|
+
) -> t.Callable[[t.Callable[..., t.Any]], t.Callable[..., t.Any]]:
|
|
95
|
+
def decorator(func: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]:
|
|
96
|
+
cache: dict[str, tuple[float, t.Any]] = {}
|
|
97
|
+
|
|
98
|
+
@functools.wraps(func)
|
|
99
|
+
def wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any:
|
|
100
|
+
key = str(args) + str(sorted(kwargs.items()))
|
|
101
|
+
if key in cache:
|
|
102
|
+
timestamp, value = cache[key]
|
|
103
|
+
if time.time() - timestamp <= ttl:
|
|
104
|
+
return value
|
|
105
|
+
del cache[key]
|
|
106
|
+
result = func(*args, **kwargs)
|
|
107
|
+
cache[key] = (time.time(), result)
|
|
108
|
+
return result
|
|
109
|
+
|
|
110
|
+
wrapper.cache_clear = cache.clear # type: ignore[attr-defined]
|
|
111
|
+
wrapper.cache_info = lambda: {"size": len(cache), "ttl": ttl} # type: ignore[attr-defined]
|
|
112
|
+
return wrapper
|
|
113
|
+
|
|
114
|
+
return decorator
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def batch_file_operations(
|
|
118
|
+
operations: list[t.Callable[[], t.Any]],
|
|
119
|
+
batch_size: int = 50,
|
|
120
|
+
) -> list[t.Any]:
|
|
121
|
+
results: list[t.Any] = []
|
|
122
|
+
for i in range(0, len(operations), batch_size):
|
|
123
|
+
batch = operations[i : i + batch_size]
|
|
124
|
+
batch_results: list[t.Any] = []
|
|
125
|
+
for operation in batch:
|
|
126
|
+
try:
|
|
127
|
+
result = operation()
|
|
128
|
+
batch_results.append(result)
|
|
129
|
+
except Exception as e:
|
|
130
|
+
batch_results.append(e)
|
|
131
|
+
results.extend(batch_results)
|
|
132
|
+
if i + batch_size < len(operations):
|
|
133
|
+
time.sleep(0.01)
|
|
134
|
+
|
|
135
|
+
return results
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class OptimizedFileWatcher:
|
|
139
|
+
def __init__(self, root_path: Path) -> None:
|
|
140
|
+
self.root_path = root_path
|
|
141
|
+
self._file_cache = FileCache(ttl=60.0)
|
|
142
|
+
|
|
143
|
+
@memoize_with_ttl(ttl=30.0)
|
|
144
|
+
def get_python_files(self) -> list[Path]:
|
|
145
|
+
return list(self.root_path.rglob("*.py"))
|
|
146
|
+
|
|
147
|
+
def get_modified_files(self, since: float) -> list[Path]:
|
|
148
|
+
cache_key = f"modified_since_{since}"
|
|
149
|
+
cached = self._file_cache.get(cache_key)
|
|
150
|
+
if cached is not None:
|
|
151
|
+
return cached
|
|
152
|
+
modified_files: list[Path] = []
|
|
153
|
+
for py_file in self.get_python_files():
|
|
154
|
+
try:
|
|
155
|
+
if py_file.stat().st_mtime > since:
|
|
156
|
+
modified_files.append(py_file)
|
|
157
|
+
except (OSError, FileNotFoundError):
|
|
158
|
+
continue
|
|
159
|
+
self._file_cache.set(cache_key, modified_files)
|
|
160
|
+
return modified_files
|
|
161
|
+
|
|
162
|
+
def clear_cache(self) -> None:
|
|
163
|
+
self._file_cache.clear()
|
|
164
|
+
|
|
165
|
+
if hasattr(self.get_python_files, "cache_clear"):
|
|
166
|
+
self.get_python_files.cache_clear() # type: ignore[attr-defined]
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class ParallelTaskExecutor:
|
|
170
|
+
def __init__(self, max_workers: int | None = None) -> None:
|
|
171
|
+
import os
|
|
172
|
+
|
|
173
|
+
self.max_workers = max_workers or min(os.cpu_count() or 1, 8)
|
|
174
|
+
|
|
175
|
+
def execute_tasks(
|
|
176
|
+
self,
|
|
177
|
+
tasks: list[t.Callable[[], t.Any]],
|
|
178
|
+
timeout: float = 300.0,
|
|
179
|
+
) -> list[t.Any]:
|
|
180
|
+
if len(tasks) <= 1:
|
|
181
|
+
return [task() for task in tasks]
|
|
182
|
+
import concurrent.futures
|
|
183
|
+
|
|
184
|
+
results: list[tuple[int, t.Any]] = []
|
|
185
|
+
with concurrent.futures.ThreadPoolExecutor(
|
|
186
|
+
max_workers=self.max_workers,
|
|
187
|
+
) as executor:
|
|
188
|
+
future_to_task = {executor.submit(task): i for i, task in enumerate(tasks)}
|
|
189
|
+
for future in concurrent.futures.as_completed(
|
|
190
|
+
future_to_task,
|
|
191
|
+
timeout=timeout,
|
|
192
|
+
):
|
|
193
|
+
task_index = future_to_task[future]
|
|
194
|
+
try:
|
|
195
|
+
result = future.result()
|
|
196
|
+
results.append((task_index, result))
|
|
197
|
+
except Exception as e:
|
|
198
|
+
results.append((task_index, e))
|
|
199
|
+
import operator
|
|
200
|
+
|
|
201
|
+
results.sort(key=operator.itemgetter(0))
|
|
202
|
+
return [result for _, result in results]
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def optimize_subprocess_calls(
|
|
206
|
+
commands: list[list[str]],
|
|
207
|
+
cwd: Path | None = None,
|
|
208
|
+
) -> list[t.Any]:
|
|
209
|
+
if len(commands) <= 1:
|
|
210
|
+
import subprocess
|
|
211
|
+
|
|
212
|
+
return [
|
|
213
|
+
subprocess.run(cmd, check=False, cwd=cwd, capture_output=True, text=True)
|
|
214
|
+
for cmd in commands
|
|
215
|
+
]
|
|
216
|
+
executor = ParallelTaskExecutor()
|
|
217
|
+
|
|
218
|
+
def run_command(cmd: list[str]) -> t.Any:
|
|
219
|
+
import subprocess
|
|
220
|
+
|
|
221
|
+
return subprocess.run(cmd, check=False, cwd=cwd, capture_output=True, text=True)
|
|
222
|
+
|
|
223
|
+
import functools
|
|
224
|
+
|
|
225
|
+
tasks: list[t.Callable[[], t.Any]] = [
|
|
226
|
+
functools.partial(run_command, cmd) for cmd in commands
|
|
227
|
+
]
|
|
228
|
+
return executor.execute_tasks(tasks)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
_performance_monitor: PerformanceMonitor | None = None
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def get_performance_monitor() -> PerformanceMonitor:
|
|
235
|
+
global _performance_monitor
|
|
236
|
+
if _performance_monitor is None:
|
|
237
|
+
_performance_monitor = PerformanceMonitor()
|
|
238
|
+
return _performance_monitor
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def reset_performance_monitor() -> None:
|
|
242
|
+
global _performance_monitor
|
|
243
|
+
_performance_monitor = None
|