crackerjack 0.33.0__py3-none-any.whl ā 0.33.1__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/__main__.py +1350 -34
- crackerjack/adapters/__init__.py +17 -0
- crackerjack/adapters/lsp_client.py +358 -0
- crackerjack/adapters/rust_tool_adapter.py +194 -0
- crackerjack/adapters/rust_tool_manager.py +193 -0
- crackerjack/adapters/skylos_adapter.py +231 -0
- crackerjack/adapters/zuban_adapter.py +560 -0
- crackerjack/agents/base.py +7 -3
- crackerjack/agents/coordinator.py +271 -33
- crackerjack/agents/documentation_agent.py +9 -15
- crackerjack/agents/dry_agent.py +3 -15
- crackerjack/agents/formatting_agent.py +1 -1
- crackerjack/agents/import_optimization_agent.py +36 -180
- crackerjack/agents/performance_agent.py +17 -98
- crackerjack/agents/performance_helpers.py +7 -31
- crackerjack/agents/proactive_agent.py +1 -3
- crackerjack/agents/refactoring_agent.py +16 -85
- crackerjack/agents/refactoring_helpers.py +7 -42
- crackerjack/agents/security_agent.py +9 -48
- crackerjack/agents/test_creation_agent.py +356 -513
- crackerjack/agents/test_specialist_agent.py +0 -4
- crackerjack/api.py +6 -25
- crackerjack/cli/cache_handlers.py +204 -0
- crackerjack/cli/cache_handlers_enhanced.py +683 -0
- crackerjack/cli/facade.py +100 -0
- crackerjack/cli/handlers.py +224 -9
- crackerjack/cli/interactive.py +6 -4
- crackerjack/cli/options.py +642 -55
- crackerjack/cli/utils.py +2 -1
- crackerjack/code_cleaner.py +58 -117
- crackerjack/config/global_lock_config.py +8 -48
- crackerjack/config/hooks.py +53 -62
- crackerjack/core/async_workflow_orchestrator.py +24 -34
- crackerjack/core/autofix_coordinator.py +3 -17
- crackerjack/core/enhanced_container.py +4 -13
- crackerjack/core/file_lifecycle.py +12 -89
- crackerjack/core/performance.py +2 -2
- crackerjack/core/performance_monitor.py +15 -55
- crackerjack/core/phase_coordinator.py +104 -204
- crackerjack/core/resource_manager.py +14 -90
- crackerjack/core/service_watchdog.py +62 -95
- crackerjack/core/session_coordinator.py +149 -0
- crackerjack/core/timeout_manager.py +14 -72
- crackerjack/core/websocket_lifecycle.py +13 -78
- crackerjack/core/workflow_orchestrator.py +171 -174
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
- crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
- crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
- crackerjack/docs/generated/api/SERVICES.md +1252 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +765 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +977 -0
- crackerjack/dynamic_config.py +55 -50
- crackerjack/executors/async_hook_executor.py +10 -15
- crackerjack/executors/cached_hook_executor.py +117 -43
- crackerjack/executors/hook_executor.py +8 -34
- crackerjack/executors/hook_lock_manager.py +26 -183
- crackerjack/executors/individual_hook_executor.py +13 -11
- crackerjack/executors/lsp_aware_hook_executor.py +270 -0
- crackerjack/executors/tool_proxy.py +417 -0
- crackerjack/hooks/lsp_hook.py +79 -0
- crackerjack/intelligence/adaptive_learning.py +25 -10
- crackerjack/intelligence/agent_orchestrator.py +2 -5
- crackerjack/intelligence/agent_registry.py +34 -24
- crackerjack/intelligence/agent_selector.py +5 -7
- crackerjack/interactive.py +17 -6
- crackerjack/managers/async_hook_manager.py +0 -1
- crackerjack/managers/hook_manager.py +79 -1
- crackerjack/managers/publish_manager.py +44 -8
- crackerjack/managers/test_command_builder.py +1 -15
- crackerjack/managers/test_executor.py +1 -3
- crackerjack/managers/test_manager.py +98 -7
- crackerjack/managers/test_manager_backup.py +10 -9
- crackerjack/mcp/cache.py +2 -2
- crackerjack/mcp/client_runner.py +1 -1
- crackerjack/mcp/context.py +191 -68
- crackerjack/mcp/dashboard.py +7 -5
- crackerjack/mcp/enhanced_progress_monitor.py +31 -28
- crackerjack/mcp/file_monitor.py +30 -23
- crackerjack/mcp/progress_components.py +31 -21
- crackerjack/mcp/progress_monitor.py +50 -53
- crackerjack/mcp/rate_limiter.py +6 -6
- crackerjack/mcp/server_core.py +17 -16
- crackerjack/mcp/service_watchdog.py +2 -1
- crackerjack/mcp/state.py +4 -7
- crackerjack/mcp/task_manager.py +11 -9
- crackerjack/mcp/tools/core_tools.py +173 -32
- crackerjack/mcp/tools/error_analyzer.py +3 -2
- crackerjack/mcp/tools/execution_tools.py +8 -10
- crackerjack/mcp/tools/execution_tools_backup.py +42 -30
- crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
- crackerjack/mcp/tools/intelligence_tools.py +5 -2
- crackerjack/mcp/tools/monitoring_tools.py +33 -70
- crackerjack/mcp/tools/proactive_tools.py +24 -11
- crackerjack/mcp/tools/progress_tools.py +5 -8
- crackerjack/mcp/tools/utility_tools.py +20 -14
- crackerjack/mcp/tools/workflow_executor.py +62 -40
- crackerjack/mcp/websocket/app.py +8 -0
- crackerjack/mcp/websocket/endpoints.py +352 -357
- crackerjack/mcp/websocket/jobs.py +40 -57
- crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
- crackerjack/mcp/websocket/server.py +7 -25
- crackerjack/mcp/websocket/websocket_handler.py +6 -17
- crackerjack/mixins/__init__.py +0 -2
- crackerjack/mixins/error_handling.py +1 -70
- crackerjack/models/config.py +12 -1
- crackerjack/models/config_adapter.py +49 -1
- crackerjack/models/protocols.py +122 -122
- crackerjack/models/resource_protocols.py +55 -210
- crackerjack/monitoring/ai_agent_watchdog.py +13 -13
- crackerjack/monitoring/metrics_collector.py +426 -0
- crackerjack/monitoring/regression_prevention.py +8 -8
- crackerjack/monitoring/websocket_server.py +643 -0
- crackerjack/orchestration/advanced_orchestrator.py +11 -6
- crackerjack/orchestration/coverage_improvement.py +3 -3
- crackerjack/orchestration/execution_strategies.py +26 -6
- crackerjack/orchestration/test_progress_streamer.py +8 -5
- crackerjack/plugins/base.py +2 -2
- crackerjack/plugins/hooks.py +7 -0
- crackerjack/plugins/managers.py +11 -8
- crackerjack/security/__init__.py +0 -1
- crackerjack/security/audit.py +6 -35
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +615 -0
- crackerjack/services/backup_service.py +2 -2
- crackerjack/services/bounded_status_operations.py +15 -152
- crackerjack/services/cache.py +127 -1
- crackerjack/services/changelog_automation.py +395 -0
- crackerjack/services/config.py +15 -9
- crackerjack/services/config_merge.py +19 -80
- crackerjack/services/config_template.py +506 -0
- crackerjack/services/contextual_ai_assistant.py +48 -22
- crackerjack/services/coverage_badge_service.py +171 -0
- crackerjack/services/coverage_ratchet.py +27 -25
- crackerjack/services/debug.py +3 -3
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +14 -11
- crackerjack/services/documentation_generator.py +491 -0
- crackerjack/services/documentation_service.py +675 -0
- crackerjack/services/enhanced_filesystem.py +6 -5
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_hasher.py +1 -1
- crackerjack/services/git.py +8 -25
- crackerjack/services/health_metrics.py +10 -8
- crackerjack/services/heatmap_generator.py +735 -0
- crackerjack/services/initialization.py +11 -30
- crackerjack/services/input_validator.py +5 -97
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +15 -12
- crackerjack/services/logging.py +4 -3
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +19 -87
- crackerjack/services/metrics.py +42 -33
- crackerjack/services/parallel_executor.py +9 -67
- crackerjack/services/pattern_cache.py +1 -1
- crackerjack/services/pattern_detector.py +6 -6
- crackerjack/services/performance_benchmarks.py +18 -59
- crackerjack/services/performance_cache.py +20 -81
- crackerjack/services/performance_monitor.py +27 -95
- crackerjack/services/predictive_analytics.py +510 -0
- crackerjack/services/quality_baseline.py +234 -0
- crackerjack/services/quality_baseline_enhanced.py +646 -0
- crackerjack/services/quality_intelligence.py +785 -0
- crackerjack/services/regex_patterns.py +605 -524
- crackerjack/services/regex_utils.py +43 -123
- crackerjack/services/secure_path_utils.py +5 -164
- crackerjack/services/secure_status_formatter.py +30 -141
- crackerjack/services/secure_subprocess.py +11 -92
- crackerjack/services/security.py +9 -41
- crackerjack/services/security_logger.py +12 -24
- crackerjack/services/server_manager.py +124 -16
- crackerjack/services/status_authentication.py +16 -159
- crackerjack/services/status_security_manager.py +4 -131
- crackerjack/services/thread_safe_status_collector.py +19 -125
- crackerjack/services/unified_config.py +21 -13
- crackerjack/services/validation_rate_limiter.py +5 -54
- crackerjack/services/version_analyzer.py +459 -0
- crackerjack/services/version_checker.py +1 -1
- crackerjack/services/websocket_resource_limiter.py +10 -144
- crackerjack/services/zuban_lsp_service.py +390 -0
- crackerjack/slash_commands/__init__.py +2 -7
- crackerjack/slash_commands/run.md +2 -2
- crackerjack/tools/validate_input_validator_patterns.py +14 -40
- crackerjack/tools/validate_regex_patterns.py +19 -48
- {crackerjack-0.33.0.dist-info ā crackerjack-0.33.1.dist-info}/METADATA +196 -25
- crackerjack-0.33.1.dist-info/RECORD +229 -0
- crackerjack/CLAUDE.md +0 -207
- crackerjack/RULES.md +0 -380
- crackerjack/py313.py +0 -234
- crackerjack-0.33.0.dist-info/RECORD +0 -187
- {crackerjack-0.33.0.dist-info ā crackerjack-0.33.1.dist-info}/WHEEL +0 -0
- {crackerjack-0.33.0.dist-info ā crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.33.0.dist-info ā crackerjack-0.33.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
"""Resource lifecycle management protocols.
|
|
2
|
-
|
|
3
|
-
Defines protocols and interfaces for comprehensive resource management
|
|
4
|
-
patterns throughout the crackerjack codebase.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
1
|
import typing as t
|
|
8
2
|
from abc import ABC, abstractmethod
|
|
9
3
|
from types import TracebackType
|
|
@@ -14,184 +8,104 @@ if t.TYPE_CHECKING:
|
|
|
14
8
|
|
|
15
9
|
|
|
16
10
|
class AsyncCleanupProtocol(t.Protocol):
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
async def cleanup(self) -> None:
|
|
20
|
-
"""Clean up the resource asynchronously."""
|
|
21
|
-
...
|
|
11
|
+
async def cleanup(self) -> None: ...
|
|
22
12
|
|
|
23
13
|
|
|
24
14
|
class SyncCleanupProtocol(t.Protocol):
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def cleanup(self) -> None:
|
|
28
|
-
"""Clean up the resource synchronously."""
|
|
29
|
-
...
|
|
15
|
+
def cleanup(self) -> None: ...
|
|
30
16
|
|
|
31
17
|
|
|
32
18
|
class ResourceLifecycleProtocol(t.Protocol):
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
async def initialize(self) -> None:
|
|
36
|
-
"""Initialize the resource."""
|
|
37
|
-
...
|
|
19
|
+
async def initialize(self) -> None: ...
|
|
38
20
|
|
|
39
|
-
async def cleanup(self) -> None:
|
|
40
|
-
"""Clean up the resource."""
|
|
41
|
-
...
|
|
21
|
+
async def cleanup(self) -> None: ...
|
|
42
22
|
|
|
43
|
-
def is_initialized(self) -> bool:
|
|
44
|
-
"""Check if the resource is initialized."""
|
|
45
|
-
...
|
|
23
|
+
def is_initialized(self) -> bool: ...
|
|
46
24
|
|
|
47
|
-
def is_closed(self) -> bool:
|
|
48
|
-
"""Check if the resource is closed."""
|
|
49
|
-
...
|
|
25
|
+
def is_closed(self) -> bool: ...
|
|
50
26
|
|
|
51
27
|
|
|
52
28
|
class AsyncContextProtocol(t.Protocol):
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
async def __aenter__(self) -> t.Self:
|
|
56
|
-
"""Enter async context."""
|
|
57
|
-
...
|
|
29
|
+
async def __aenter__(self) -> t.Self: ...
|
|
58
30
|
|
|
59
31
|
async def __aexit__(
|
|
60
32
|
self,
|
|
61
33
|
exc_type: type[BaseException] | None,
|
|
62
34
|
exc_val: BaseException | None,
|
|
63
35
|
exc_tb: TracebackType | None,
|
|
64
|
-
) -> None:
|
|
65
|
-
"""Exit async context."""
|
|
66
|
-
...
|
|
36
|
+
) -> None: ...
|
|
67
37
|
|
|
68
38
|
|
|
69
39
|
class ResourceManagerProtocol(t.Protocol):
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def register_resource(self, resource: AsyncCleanupProtocol) -> None:
|
|
73
|
-
"""Register a resource for management."""
|
|
74
|
-
...
|
|
40
|
+
def register_resource(self, resource: AsyncCleanupProtocol) -> None: ...
|
|
75
41
|
|
|
76
42
|
def register_cleanup_callback(
|
|
77
43
|
self, callback: t.Callable[[], t.Awaitable[None]]
|
|
78
|
-
) -> None:
|
|
79
|
-
"""Register a cleanup callback."""
|
|
80
|
-
...
|
|
44
|
+
) -> None: ...
|
|
81
45
|
|
|
82
|
-
async def cleanup_all(self) -> None:
|
|
83
|
-
"""Clean up all managed resources."""
|
|
84
|
-
...
|
|
46
|
+
async def cleanup_all(self) -> None: ...
|
|
85
47
|
|
|
86
48
|
|
|
87
49
|
class FileResourceProtocol(t.Protocol):
|
|
88
|
-
"""Protocol for file-based resources."""
|
|
89
|
-
|
|
90
50
|
@property
|
|
91
|
-
def path(self) -> "Path":
|
|
92
|
-
"""Get the file path."""
|
|
93
|
-
...
|
|
51
|
+
def path(self) -> "Path": ...
|
|
94
52
|
|
|
95
|
-
def exists(self) -> bool:
|
|
96
|
-
"""Check if the file exists."""
|
|
97
|
-
...
|
|
53
|
+
def exists(self) -> bool: ...
|
|
98
54
|
|
|
99
|
-
async def cleanup(self) -> None:
|
|
100
|
-
"""Clean up the file resource."""
|
|
101
|
-
...
|
|
55
|
+
async def cleanup(self) -> None: ...
|
|
102
56
|
|
|
103
57
|
|
|
104
58
|
class ProcessResourceProtocol(t.Protocol):
|
|
105
|
-
"""Protocol for process-based resources."""
|
|
106
|
-
|
|
107
59
|
@property
|
|
108
|
-
def pid(self) -> int:
|
|
109
|
-
"""Get the process ID."""
|
|
110
|
-
...
|
|
60
|
+
def pid(self) -> int: ...
|
|
111
61
|
|
|
112
|
-
def is_running(self) -> bool:
|
|
113
|
-
"""Check if the process is running."""
|
|
114
|
-
...
|
|
62
|
+
def is_running(self) -> bool: ...
|
|
115
63
|
|
|
116
|
-
async def cleanup(self) -> None:
|
|
117
|
-
"""Clean up the process resource."""
|
|
118
|
-
...
|
|
64
|
+
async def cleanup(self) -> None: ...
|
|
119
65
|
|
|
120
66
|
|
|
121
67
|
class TaskResourceProtocol(t.Protocol):
|
|
122
|
-
"""Protocol for asyncio task resources."""
|
|
123
|
-
|
|
124
68
|
@property
|
|
125
|
-
def task(self) -> "asyncio.Task[t.Any]":
|
|
126
|
-
"""Get the asyncio task."""
|
|
127
|
-
...
|
|
69
|
+
def task(self) -> "asyncio.Task[t.Any]": ...
|
|
128
70
|
|
|
129
|
-
def is_done(self) -> bool:
|
|
130
|
-
"""Check if the task is done."""
|
|
131
|
-
...
|
|
71
|
+
def is_done(self) -> bool: ...
|
|
132
72
|
|
|
133
|
-
def is_cancelled(self) -> bool:
|
|
134
|
-
"""Check if the task is cancelled."""
|
|
135
|
-
...
|
|
73
|
+
def is_cancelled(self) -> bool: ...
|
|
136
74
|
|
|
137
|
-
async def cleanup(self) -> None:
|
|
138
|
-
"""Clean up the task resource."""
|
|
139
|
-
...
|
|
75
|
+
async def cleanup(self) -> None: ...
|
|
140
76
|
|
|
141
77
|
|
|
142
78
|
class NetworkResourceProtocol(t.Protocol):
|
|
143
|
-
"""Protocol for network-based resources."""
|
|
144
|
-
|
|
145
79
|
@property
|
|
146
|
-
def is_connected(self) -> bool:
|
|
147
|
-
"""Check if the resource is connected."""
|
|
148
|
-
...
|
|
80
|
+
def is_connected(self) -> bool: ...
|
|
149
81
|
|
|
150
|
-
async def disconnect(self) -> None:
|
|
151
|
-
"""Disconnect the resource."""
|
|
152
|
-
...
|
|
82
|
+
async def disconnect(self) -> None: ...
|
|
153
83
|
|
|
154
|
-
async def cleanup(self) -> None:
|
|
155
|
-
"""Clean up the network resource."""
|
|
156
|
-
...
|
|
84
|
+
async def cleanup(self) -> None: ...
|
|
157
85
|
|
|
158
86
|
|
|
159
87
|
class CacheResourceProtocol(t.Protocol):
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def clear(self) -> None:
|
|
163
|
-
"""Clear the cache."""
|
|
164
|
-
...
|
|
88
|
+
def clear(self) -> None: ...
|
|
165
89
|
|
|
166
|
-
def get_size(self) -> int:
|
|
167
|
-
"""Get the cache size."""
|
|
168
|
-
...
|
|
90
|
+
def get_size(self) -> int: ...
|
|
169
91
|
|
|
170
|
-
async def cleanup(self) -> None:
|
|
171
|
-
"""Clean up the cache resource."""
|
|
172
|
-
...
|
|
92
|
+
async def cleanup(self) -> None: ...
|
|
173
93
|
|
|
174
94
|
|
|
175
|
-
# Abstract base classes for resource implementations
|
|
176
95
|
class AbstractManagedResource(ABC):
|
|
177
|
-
"""Abstract base class for managed resources."""
|
|
178
|
-
|
|
179
96
|
def __init__(self) -> None:
|
|
180
97
|
self._initialized = False
|
|
181
98
|
self._closed = False
|
|
182
99
|
|
|
183
100
|
@abstractmethod
|
|
184
101
|
async def _do_initialize(self) -> None:
|
|
185
|
-
"""Perform resource initialization. Override in subclasses."""
|
|
186
102
|
pass
|
|
187
103
|
|
|
188
104
|
@abstractmethod
|
|
189
105
|
async def _do_cleanup(self) -> None:
|
|
190
|
-
"""Perform resource cleanup. Override in subclasses."""
|
|
191
106
|
pass
|
|
192
107
|
|
|
193
108
|
async def initialize(self) -> None:
|
|
194
|
-
"""Initialize the resource if not already initialized."""
|
|
195
109
|
if self._initialized:
|
|
196
110
|
return
|
|
197
111
|
|
|
@@ -203,7 +117,6 @@ class AbstractManagedResource(ABC):
|
|
|
203
117
|
raise
|
|
204
118
|
|
|
205
119
|
async def cleanup(self) -> None:
|
|
206
|
-
"""Clean up the resource if not already closed."""
|
|
207
120
|
if self._closed:
|
|
208
121
|
return
|
|
209
122
|
|
|
@@ -211,7 +124,6 @@ class AbstractManagedResource(ABC):
|
|
|
211
124
|
try:
|
|
212
125
|
await self._do_cleanup()
|
|
213
126
|
except Exception:
|
|
214
|
-
# Log but don't re-raise during cleanup
|
|
215
127
|
import logging
|
|
216
128
|
|
|
217
129
|
logging.getLogger(__name__).warning(
|
|
@@ -219,15 +131,12 @@ class AbstractManagedResource(ABC):
|
|
|
219
131
|
)
|
|
220
132
|
|
|
221
133
|
def is_initialized(self) -> bool:
|
|
222
|
-
"""Check if the resource is initialized."""
|
|
223
134
|
return self._initialized
|
|
224
135
|
|
|
225
136
|
def is_closed(self) -> bool:
|
|
226
|
-
"""Check if the resource is closed."""
|
|
227
137
|
return self._closed
|
|
228
138
|
|
|
229
139
|
async def __aenter__(self) -> t.Self:
|
|
230
|
-
"""Enter async context and initialize."""
|
|
231
140
|
await self.initialize()
|
|
232
141
|
return self
|
|
233
142
|
|
|
@@ -237,41 +146,32 @@ class AbstractManagedResource(ABC):
|
|
|
237
146
|
exc_val: BaseException | None,
|
|
238
147
|
exc_tb: TracebackType | None,
|
|
239
148
|
) -> None:
|
|
240
|
-
"""Exit async context and cleanup."""
|
|
241
149
|
await self.cleanup()
|
|
242
150
|
|
|
243
151
|
|
|
244
152
|
class AbstractFileResource(AbstractManagedResource):
|
|
245
|
-
"""Abstract base class for file-based resources."""
|
|
246
|
-
|
|
247
153
|
def __init__(self, path: "Path") -> None:
|
|
248
154
|
super().__init__()
|
|
249
155
|
self._path = path
|
|
250
156
|
|
|
251
157
|
@property
|
|
252
158
|
def path(self) -> "Path":
|
|
253
|
-
"""Get the file path."""
|
|
254
159
|
return self._path
|
|
255
160
|
|
|
256
161
|
def exists(self) -> bool:
|
|
257
|
-
"""Check if the file exists."""
|
|
258
162
|
return self._path.exists()
|
|
259
163
|
|
|
260
164
|
|
|
261
165
|
class AbstractProcessResource(AbstractManagedResource):
|
|
262
|
-
"""Abstract base class for process-based resources."""
|
|
263
|
-
|
|
264
166
|
def __init__(self, pid: int) -> None:
|
|
265
167
|
super().__init__()
|
|
266
168
|
self._pid = pid
|
|
267
169
|
|
|
268
170
|
@property
|
|
269
171
|
def pid(self) -> int:
|
|
270
|
-
"""Get the process ID."""
|
|
271
172
|
return self._pid
|
|
272
173
|
|
|
273
174
|
def is_running(self) -> bool:
|
|
274
|
-
"""Check if the process is running."""
|
|
275
175
|
try:
|
|
276
176
|
import os
|
|
277
177
|
|
|
@@ -282,59 +182,49 @@ class AbstractProcessResource(AbstractManagedResource):
|
|
|
282
182
|
|
|
283
183
|
|
|
284
184
|
class AbstractTaskResource(AbstractManagedResource):
|
|
285
|
-
"""Abstract base class for asyncio task resources."""
|
|
286
|
-
|
|
287
185
|
def __init__(self, task: "asyncio.Task[t.Any]") -> None:
|
|
288
186
|
super().__init__()
|
|
289
187
|
self._task = task
|
|
290
188
|
|
|
291
189
|
@property
|
|
292
190
|
def task(self) -> "asyncio.Task[t.Any]":
|
|
293
|
-
"""Get the asyncio task."""
|
|
294
191
|
return self._task
|
|
295
192
|
|
|
296
193
|
def is_done(self) -> bool:
|
|
297
|
-
"""Check if the task is done."""
|
|
298
194
|
return self._task.done()
|
|
299
195
|
|
|
300
196
|
def is_cancelled(self) -> bool:
|
|
301
|
-
"""Check if the task is cancelled."""
|
|
302
197
|
return self._task.cancelled()
|
|
303
198
|
|
|
304
199
|
|
|
305
200
|
class AbstractNetworkResource(AbstractManagedResource):
|
|
306
|
-
"""Abstract base class for network-based resources."""
|
|
307
|
-
|
|
308
201
|
def __init__(self) -> None:
|
|
309
202
|
super().__init__()
|
|
310
203
|
self._connected = False
|
|
311
204
|
|
|
312
205
|
@property
|
|
313
206
|
def is_connected(self) -> bool:
|
|
314
|
-
"""Check if the resource is connected."""
|
|
315
207
|
return self._connected and not self._closed
|
|
316
208
|
|
|
317
209
|
async def disconnect(self) -> None:
|
|
318
|
-
"""Disconnect the resource."""
|
|
319
210
|
if self._connected:
|
|
320
211
|
self._connected = False
|
|
321
212
|
await self._do_disconnect()
|
|
322
213
|
|
|
323
214
|
@abstractmethod
|
|
324
215
|
async def _do_disconnect(self) -> None:
|
|
325
|
-
"""Perform disconnection. Override in subclasses."""
|
|
326
216
|
pass
|
|
327
217
|
|
|
328
218
|
async def _do_cleanup(self) -> None:
|
|
329
|
-
"""Cleanup includes disconnection."""
|
|
330
219
|
await self.disconnect()
|
|
331
220
|
|
|
332
221
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
222
|
+
def with_resource_cleanup(
|
|
223
|
+
resource_attr: str,
|
|
224
|
+
) -> t.Callable[..., t.Callable[..., t.Awaitable[t.Any]]]:
|
|
225
|
+
def decorator(
|
|
226
|
+
func: t.Callable[..., t.Awaitable[t.Any]],
|
|
227
|
+
) -> t.Callable[..., t.Awaitable[t.Any]]:
|
|
338
228
|
async def wrapper(self: t.Any, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
|
339
229
|
resource = getattr(self, resource_attr, None)
|
|
340
230
|
try:
|
|
@@ -348,10 +238,12 @@ def with_resource_cleanup(resource_attr: str):
|
|
|
348
238
|
return decorator
|
|
349
239
|
|
|
350
240
|
|
|
351
|
-
def ensure_initialized(
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
def decorator(
|
|
241
|
+
def ensure_initialized(
|
|
242
|
+
resource_attr: str,
|
|
243
|
+
) -> t.Callable[..., t.Callable[..., t.Awaitable[t.Any]]]:
|
|
244
|
+
def decorator(
|
|
245
|
+
func: t.Callable[..., t.Awaitable[t.Any]],
|
|
246
|
+
) -> t.Callable[..., t.Awaitable[t.Any]]:
|
|
355
247
|
async def wrapper(self: t.Any, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
|
356
248
|
resource = getattr(self, resource_attr, None)
|
|
357
249
|
if resource and hasattr(resource, "initialize"):
|
|
@@ -363,92 +255,45 @@ def ensure_initialized(resource_attr: str):
|
|
|
363
255
|
return decorator
|
|
364
256
|
|
|
365
257
|
|
|
366
|
-
# Resource health monitoring protocols
|
|
367
258
|
class HealthCheckProtocol(t.Protocol):
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
async def health_check(self) -> dict[str, t.Any]:
|
|
371
|
-
"""Perform health check and return status."""
|
|
372
|
-
...
|
|
259
|
+
async def health_check(self) -> dict[str, t.Any]: ...
|
|
373
260
|
|
|
374
|
-
def is_healthy(self) -> bool:
|
|
375
|
-
"""Quick health check."""
|
|
376
|
-
...
|
|
261
|
+
def is_healthy(self) -> bool: ...
|
|
377
262
|
|
|
378
263
|
|
|
379
264
|
class MonitorableResourceProtocol(t.Protocol):
|
|
380
|
-
|
|
265
|
+
def get_metrics(self) -> dict[str, t.Any]: ...
|
|
381
266
|
|
|
382
|
-
def
|
|
383
|
-
"""Get resource metrics."""
|
|
384
|
-
...
|
|
267
|
+
def get_status(self) -> str: ...
|
|
385
268
|
|
|
386
|
-
def
|
|
387
|
-
"""Get resource status string."""
|
|
388
|
-
...
|
|
269
|
+
async def health_check(self) -> dict[str, t.Any]: ...
|
|
389
270
|
|
|
390
|
-
async def health_check(self) -> dict[str, t.Any]:
|
|
391
|
-
"""Perform health check."""
|
|
392
|
-
...
|
|
393
271
|
|
|
394
|
-
|
|
395
|
-
# Resource factory protocols
|
|
396
272
|
class ResourceFactoryProtocol(t.Protocol):
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
async def create_resource(self, **kwargs: t.Any) -> AsyncCleanupProtocol:
|
|
400
|
-
"""Create a new resource instance."""
|
|
401
|
-
...
|
|
273
|
+
async def create_resource(self, **kwargs: t.Any) -> AsyncCleanupProtocol: ...
|
|
402
274
|
|
|
403
|
-
def get_resource_type(self) -> str:
|
|
404
|
-
"""Get the resource type name."""
|
|
405
|
-
...
|
|
275
|
+
def get_resource_type(self) -> str: ...
|
|
406
276
|
|
|
407
277
|
|
|
408
278
|
class PooledResourceProtocol(t.Protocol):
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
async def acquire(self) -> AsyncCleanupProtocol:
|
|
412
|
-
"""Acquire a resource from the pool."""
|
|
413
|
-
...
|
|
279
|
+
async def acquire(self) -> AsyncCleanupProtocol: ...
|
|
414
280
|
|
|
415
|
-
async def release(self, resource: AsyncCleanupProtocol) -> None:
|
|
416
|
-
"""Release a resource back to the pool."""
|
|
417
|
-
...
|
|
281
|
+
async def release(self, resource: AsyncCleanupProtocol) -> None: ...
|
|
418
282
|
|
|
419
|
-
def get_pool_size(self) -> int:
|
|
420
|
-
"""Get current pool size."""
|
|
421
|
-
...
|
|
283
|
+
def get_pool_size(self) -> int: ...
|
|
422
284
|
|
|
423
|
-
def get_active_count(self) -> int:
|
|
424
|
-
"""Get count of active resources."""
|
|
425
|
-
...
|
|
285
|
+
def get_active_count(self) -> int: ...
|
|
426
286
|
|
|
427
287
|
|
|
428
|
-
# Error handling protocols
|
|
429
288
|
class ResourceErrorProtocol(t.Protocol):
|
|
430
|
-
|
|
289
|
+
def handle_error(self, error: Exception) -> bool: ...
|
|
431
290
|
|
|
432
|
-
def
|
|
433
|
-
"""Handle resource error. Return True if error was handled."""
|
|
434
|
-
...
|
|
291
|
+
def should_retry(self, error: Exception) -> bool: ...
|
|
435
292
|
|
|
436
|
-
def
|
|
437
|
-
"""Check if operation should be retried on this error."""
|
|
438
|
-
...
|
|
439
|
-
|
|
440
|
-
def get_retry_delay(self, attempt: int) -> float:
|
|
441
|
-
"""Get delay before retry attempt."""
|
|
442
|
-
...
|
|
293
|
+
def get_retry_delay(self, attempt: int) -> float: ...
|
|
443
294
|
|
|
444
295
|
|
|
445
296
|
class FallbackResourceProtocol(t.Protocol):
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
async def get_fallback(self) -> AsyncCleanupProtocol | None:
|
|
449
|
-
"""Get fallback resource if primary fails."""
|
|
450
|
-
...
|
|
297
|
+
async def get_fallback(self) -> AsyncCleanupProtocol | None: ...
|
|
451
298
|
|
|
452
|
-
def has_fallback(self) -> bool:
|
|
453
|
-
"""Check if fallback is available."""
|
|
454
|
-
...
|
|
299
|
+
def has_fallback(self) -> bool: ...
|
|
@@ -21,7 +21,7 @@ class AgentPerformanceMetrics:
|
|
|
21
21
|
failed_fixes: int = 0
|
|
22
22
|
average_confidence: float = 0.0
|
|
23
23
|
average_execution_time: float = 0.0
|
|
24
|
-
issue_types_handled: dict[IssueType, int] = field(default_factory=dict)
|
|
24
|
+
issue_types_handled: dict[IssueType, int] = field(default_factory=dict[str, t.Any])
|
|
25
25
|
recent_failures: list[str] = field(default_factory=list)
|
|
26
26
|
last_successful_fix: datetime | None = None
|
|
27
27
|
regression_patterns: list[str] = field(default_factory=list)
|
|
@@ -34,7 +34,7 @@ class WatchdogAlert:
|
|
|
34
34
|
agent_name: str | None = None
|
|
35
35
|
issue_id: str | None = None
|
|
36
36
|
timestamp: datetime = field(default_factory=datetime.now)
|
|
37
|
-
details: dict[str, t.Any] = field(default_factory=dict)
|
|
37
|
+
details: dict[str, t.Any] = field(default_factory=dict[str, t.Any])
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
class AIAgentWatchdog:
|
|
@@ -54,7 +54,7 @@ class AIAgentWatchdog:
|
|
|
54
54
|
self.min_success_rate = 0.6
|
|
55
55
|
self.max_recent_failures = 3
|
|
56
56
|
|
|
57
|
-
async def start_monitoring(self, coordinator: AgentCoordinator):
|
|
57
|
+
async def start_monitoring(self, coordinator: AgentCoordinator) -> None:
|
|
58
58
|
self.monitoring_active = True
|
|
59
59
|
self.console.print("š [bold green]AI Agent Watchdog Started[/bold green]")
|
|
60
60
|
|
|
@@ -70,14 +70,14 @@ class AIAgentWatchdog:
|
|
|
70
70
|
f"š Monitoring {len(coordinator.agents)} agents: {[a.__class__.__name__ for a in coordinator.agents]}"
|
|
71
71
|
)
|
|
72
72
|
|
|
73
|
-
def stop_monitoring(self):
|
|
73
|
+
def stop_monitoring(self) -> None:
|
|
74
74
|
self.monitoring_active = False
|
|
75
75
|
self.console.print("š [bold yellow]AI Agent Watchdog Stopped[/bold yellow]")
|
|
76
76
|
self._generate_final_report()
|
|
77
77
|
|
|
78
78
|
async def monitor_issue_handling(
|
|
79
79
|
self, agent_name: str, issue: Issue, result: FixResult, execution_time: float
|
|
80
|
-
):
|
|
80
|
+
) -> None:
|
|
81
81
|
if not self.monitoring_active:
|
|
82
82
|
return
|
|
83
83
|
|
|
@@ -134,7 +134,7 @@ class AIAgentWatchdog:
|
|
|
134
134
|
result: FixResult,
|
|
135
135
|
execution_time: float,
|
|
136
136
|
metrics: AgentPerformanceMetrics,
|
|
137
|
-
):
|
|
137
|
+
) -> None:
|
|
138
138
|
alerts = []
|
|
139
139
|
|
|
140
140
|
if execution_time > self.max_execution_time:
|
|
@@ -179,14 +179,14 @@ class AIAgentWatchdog:
|
|
|
179
179
|
)
|
|
180
180
|
|
|
181
181
|
if len(metrics.recent_failures) >= self.max_recent_failures:
|
|
182
|
-
unique_failures = set(metrics.recent_failures)
|
|
182
|
+
unique_failures = set[t.Any](metrics.recent_failures)
|
|
183
183
|
if len(unique_failures) == 1:
|
|
184
184
|
alerts.append(
|
|
185
185
|
WatchdogAlert(
|
|
186
186
|
level="error",
|
|
187
187
|
message=f"Agent repeating same failure {len(metrics.recent_failures)} times",
|
|
188
188
|
agent_name=agent_name,
|
|
189
|
-
details={"repeated_failure": list(unique_failures)[0]},
|
|
189
|
+
details={"repeated_failure": list[t.Any](unique_failures)[0]},
|
|
190
190
|
)
|
|
191
191
|
)
|
|
192
192
|
|
|
@@ -194,7 +194,7 @@ class AIAgentWatchdog:
|
|
|
194
194
|
self.alerts.append(alert)
|
|
195
195
|
await self._handle_alert(alert)
|
|
196
196
|
|
|
197
|
-
async def _handle_alert(self, alert: WatchdogAlert):
|
|
197
|
+
async def _handle_alert(self, alert: WatchdogAlert) -> None:
|
|
198
198
|
colors = {"warning": "yellow", "error": "red", "critical": "bold red"}
|
|
199
199
|
color = colors.get(alert.level) or "white"
|
|
200
200
|
|
|
@@ -270,7 +270,7 @@ class AIAgentWatchdog:
|
|
|
270
270
|
cutoff = datetime.now() - timedelta(hours=hours)
|
|
271
271
|
return [alert for alert in self.alerts if alert.timestamp > cutoff]
|
|
272
272
|
|
|
273
|
-
def _generate_final_report(self):
|
|
273
|
+
def _generate_final_report(self) -> None:
|
|
274
274
|
self.console.print("\nš [bold]AI Agent Watchdog Final Report[/bold]")
|
|
275
275
|
|
|
276
276
|
total_issues = sum(
|
|
@@ -314,7 +314,7 @@ class AIAgentWatchdog:
|
|
|
314
314
|
|
|
315
315
|
self._save_monitoring_report()
|
|
316
316
|
|
|
317
|
-
def _save_monitoring_report(self):
|
|
317
|
+
def _save_monitoring_report(self) -> None:
|
|
318
318
|
report_data = {
|
|
319
319
|
"timestamp": datetime.now().isoformat(),
|
|
320
320
|
"metrics": {
|
|
@@ -351,13 +351,13 @@ class AIAgentWatchdog:
|
|
|
351
351
|
report_file = Path(".crackerjack") / "ai_agent_monitoring_report.json"
|
|
352
352
|
report_file.parent.mkdir(exist_ok=True)
|
|
353
353
|
|
|
354
|
-
with open(
|
|
354
|
+
with report_file.open("w") as f:
|
|
355
355
|
json.dump(report_data, f, indent=2)
|
|
356
356
|
|
|
357
357
|
self.console.print(f"š Detailed report saved: {report_file}")
|
|
358
358
|
|
|
359
359
|
|
|
360
|
-
async def run_agent_monitoring_demo():
|
|
360
|
+
async def run_agent_monitoring_demo() -> None:
|
|
361
361
|
console = Console()
|
|
362
362
|
watchdog = AIAgentWatchdog(console)
|
|
363
363
|
|