crackerjack 0.31.9__py3-none-any.whl → 0.31.12__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.

Files changed (155) hide show
  1. crackerjack/CLAUDE.md +288 -705
  2. crackerjack/__main__.py +22 -8
  3. crackerjack/agents/__init__.py +0 -3
  4. crackerjack/agents/architect_agent.py +0 -43
  5. crackerjack/agents/base.py +1 -9
  6. crackerjack/agents/coordinator.py +2 -148
  7. crackerjack/agents/documentation_agent.py +109 -81
  8. crackerjack/agents/dry_agent.py +122 -97
  9. crackerjack/agents/formatting_agent.py +3 -16
  10. crackerjack/agents/import_optimization_agent.py +1174 -130
  11. crackerjack/agents/performance_agent.py +956 -188
  12. crackerjack/agents/performance_helpers.py +229 -0
  13. crackerjack/agents/proactive_agent.py +1 -48
  14. crackerjack/agents/refactoring_agent.py +516 -246
  15. crackerjack/agents/refactoring_helpers.py +282 -0
  16. crackerjack/agents/security_agent.py +393 -90
  17. crackerjack/agents/test_creation_agent.py +1776 -120
  18. crackerjack/agents/test_specialist_agent.py +59 -15
  19. crackerjack/agents/tracker.py +0 -102
  20. crackerjack/api.py +145 -37
  21. crackerjack/cli/handlers.py +48 -30
  22. crackerjack/cli/interactive.py +11 -11
  23. crackerjack/cli/options.py +66 -4
  24. crackerjack/code_cleaner.py +808 -148
  25. crackerjack/config/global_lock_config.py +110 -0
  26. crackerjack/config/hooks.py +43 -64
  27. crackerjack/core/async_workflow_orchestrator.py +247 -97
  28. crackerjack/core/autofix_coordinator.py +192 -109
  29. crackerjack/core/enhanced_container.py +46 -63
  30. crackerjack/core/file_lifecycle.py +549 -0
  31. crackerjack/core/performance.py +9 -8
  32. crackerjack/core/performance_monitor.py +395 -0
  33. crackerjack/core/phase_coordinator.py +282 -95
  34. crackerjack/core/proactive_workflow.py +9 -58
  35. crackerjack/core/resource_manager.py +501 -0
  36. crackerjack/core/service_watchdog.py +490 -0
  37. crackerjack/core/session_coordinator.py +4 -8
  38. crackerjack/core/timeout_manager.py +504 -0
  39. crackerjack/core/websocket_lifecycle.py +475 -0
  40. crackerjack/core/workflow_orchestrator.py +355 -204
  41. crackerjack/dynamic_config.py +47 -6
  42. crackerjack/errors.py +3 -4
  43. crackerjack/executors/async_hook_executor.py +63 -13
  44. crackerjack/executors/cached_hook_executor.py +14 -14
  45. crackerjack/executors/hook_executor.py +100 -37
  46. crackerjack/executors/hook_lock_manager.py +856 -0
  47. crackerjack/executors/individual_hook_executor.py +120 -86
  48. crackerjack/intelligence/__init__.py +0 -7
  49. crackerjack/intelligence/adaptive_learning.py +13 -86
  50. crackerjack/intelligence/agent_orchestrator.py +15 -78
  51. crackerjack/intelligence/agent_registry.py +12 -59
  52. crackerjack/intelligence/agent_selector.py +31 -92
  53. crackerjack/intelligence/integration.py +1 -41
  54. crackerjack/interactive.py +9 -9
  55. crackerjack/managers/async_hook_manager.py +25 -8
  56. crackerjack/managers/hook_manager.py +9 -9
  57. crackerjack/managers/publish_manager.py +57 -59
  58. crackerjack/managers/test_command_builder.py +6 -36
  59. crackerjack/managers/test_executor.py +9 -61
  60. crackerjack/managers/test_manager.py +52 -62
  61. crackerjack/managers/test_manager_backup.py +77 -127
  62. crackerjack/managers/test_progress.py +4 -23
  63. crackerjack/mcp/cache.py +5 -12
  64. crackerjack/mcp/client_runner.py +10 -10
  65. crackerjack/mcp/context.py +64 -6
  66. crackerjack/mcp/dashboard.py +14 -11
  67. crackerjack/mcp/enhanced_progress_monitor.py +55 -55
  68. crackerjack/mcp/file_monitor.py +72 -42
  69. crackerjack/mcp/progress_components.py +103 -84
  70. crackerjack/mcp/progress_monitor.py +122 -49
  71. crackerjack/mcp/rate_limiter.py +12 -12
  72. crackerjack/mcp/server_core.py +16 -22
  73. crackerjack/mcp/service_watchdog.py +26 -26
  74. crackerjack/mcp/state.py +15 -0
  75. crackerjack/mcp/tools/core_tools.py +95 -39
  76. crackerjack/mcp/tools/error_analyzer.py +6 -32
  77. crackerjack/mcp/tools/execution_tools.py +1 -56
  78. crackerjack/mcp/tools/execution_tools_backup.py +35 -131
  79. crackerjack/mcp/tools/intelligence_tool_registry.py +0 -36
  80. crackerjack/mcp/tools/intelligence_tools.py +2 -55
  81. crackerjack/mcp/tools/monitoring_tools.py +308 -145
  82. crackerjack/mcp/tools/proactive_tools.py +12 -42
  83. crackerjack/mcp/tools/progress_tools.py +23 -15
  84. crackerjack/mcp/tools/utility_tools.py +3 -40
  85. crackerjack/mcp/tools/workflow_executor.py +40 -60
  86. crackerjack/mcp/websocket/app.py +0 -3
  87. crackerjack/mcp/websocket/endpoints.py +206 -268
  88. crackerjack/mcp/websocket/jobs.py +213 -66
  89. crackerjack/mcp/websocket/server.py +84 -6
  90. crackerjack/mcp/websocket/websocket_handler.py +137 -29
  91. crackerjack/models/config_adapter.py +3 -16
  92. crackerjack/models/protocols.py +162 -3
  93. crackerjack/models/resource_protocols.py +454 -0
  94. crackerjack/models/task.py +3 -3
  95. crackerjack/monitoring/__init__.py +0 -0
  96. crackerjack/monitoring/ai_agent_watchdog.py +25 -71
  97. crackerjack/monitoring/regression_prevention.py +28 -87
  98. crackerjack/orchestration/advanced_orchestrator.py +44 -78
  99. crackerjack/orchestration/coverage_improvement.py +10 -60
  100. crackerjack/orchestration/execution_strategies.py +16 -16
  101. crackerjack/orchestration/test_progress_streamer.py +61 -53
  102. crackerjack/plugins/base.py +1 -1
  103. crackerjack/plugins/managers.py +22 -20
  104. crackerjack/py313.py +65 -21
  105. crackerjack/services/backup_service.py +467 -0
  106. crackerjack/services/bounded_status_operations.py +627 -0
  107. crackerjack/services/cache.py +7 -9
  108. crackerjack/services/config.py +35 -52
  109. crackerjack/services/config_integrity.py +5 -16
  110. crackerjack/services/config_merge.py +542 -0
  111. crackerjack/services/contextual_ai_assistant.py +17 -19
  112. crackerjack/services/coverage_ratchet.py +51 -76
  113. crackerjack/services/debug.py +25 -39
  114. crackerjack/services/dependency_monitor.py +52 -50
  115. crackerjack/services/enhanced_filesystem.py +14 -11
  116. crackerjack/services/file_hasher.py +1 -1
  117. crackerjack/services/filesystem.py +1 -12
  118. crackerjack/services/git.py +78 -44
  119. crackerjack/services/health_metrics.py +31 -27
  120. crackerjack/services/initialization.py +281 -433
  121. crackerjack/services/input_validator.py +760 -0
  122. crackerjack/services/log_manager.py +16 -16
  123. crackerjack/services/logging.py +7 -6
  124. crackerjack/services/metrics.py +43 -43
  125. crackerjack/services/pattern_cache.py +2 -31
  126. crackerjack/services/pattern_detector.py +26 -63
  127. crackerjack/services/performance_benchmarks.py +20 -45
  128. crackerjack/services/regex_patterns.py +2887 -0
  129. crackerjack/services/regex_utils.py +537 -0
  130. crackerjack/services/secure_path_utils.py +683 -0
  131. crackerjack/services/secure_status_formatter.py +534 -0
  132. crackerjack/services/secure_subprocess.py +605 -0
  133. crackerjack/services/security.py +47 -10
  134. crackerjack/services/security_logger.py +492 -0
  135. crackerjack/services/server_manager.py +109 -50
  136. crackerjack/services/smart_scheduling.py +8 -25
  137. crackerjack/services/status_authentication.py +603 -0
  138. crackerjack/services/status_security_manager.py +442 -0
  139. crackerjack/services/thread_safe_status_collector.py +546 -0
  140. crackerjack/services/tool_version_service.py +1 -23
  141. crackerjack/services/unified_config.py +36 -58
  142. crackerjack/services/validation_rate_limiter.py +269 -0
  143. crackerjack/services/version_checker.py +9 -40
  144. crackerjack/services/websocket_resource_limiter.py +572 -0
  145. crackerjack/slash_commands/__init__.py +52 -2
  146. crackerjack/tools/__init__.py +0 -0
  147. crackerjack/tools/validate_input_validator_patterns.py +262 -0
  148. crackerjack/tools/validate_regex_patterns.py +198 -0
  149. {crackerjack-0.31.9.dist-info → crackerjack-0.31.12.dist-info}/METADATA +197 -12
  150. crackerjack-0.31.12.dist-info/RECORD +178 -0
  151. crackerjack/cli/facade.py +0 -104
  152. crackerjack-0.31.9.dist-info/RECORD +0 -149
  153. {crackerjack-0.31.9.dist-info → crackerjack-0.31.12.dist-info}/WHEEL +0 -0
  154. {crackerjack-0.31.9.dist-info → crackerjack-0.31.12.dist-info}/entry_points.txt +0 -0
  155. {crackerjack-0.31.9.dist-info → crackerjack-0.31.12.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +1,12 @@
1
+ import asyncio
2
+ from contextlib import suppress
1
3
  from pathlib import Path
2
4
 
3
5
  from fastapi import FastAPI, WebSocket, WebSocketDisconnect
4
6
  from rich.console import Console
5
7
 
8
+ from crackerjack.core.timeout_manager import TimeoutStrategy, get_timeout_manager
9
+
6
10
  from .jobs import JobManager
7
11
 
8
12
  console = Console()
@@ -12,58 +16,162 @@ class WebSocketHandler:
12
16
  def __init__(self, job_manager: JobManager, progress_dir: Path) -> None:
13
17
  self.job_manager = job_manager
14
18
  self.progress_dir = progress_dir
19
+ self.timeout_manager = get_timeout_manager()
15
20
 
16
21
  async def handle_connection(self, websocket: WebSocket, job_id: str) -> None:
17
22
  if not self.job_manager.validate_job_id(job_id):
18
23
  await websocket.close(code=1008, reason="Invalid job ID")
19
24
  return
20
25
 
26
+ try:
27
+ # Add timeout to the entire connection handling
28
+ async with self.timeout_manager.timeout_context(
29
+ "websocket_connection",
30
+ timeout=3600.0, # 1 hour max connection time
31
+ strategy=TimeoutStrategy.GRACEFUL_DEGRADATION,
32
+ ):
33
+ await self._establish_connection(websocket, job_id)
34
+ await self._send_initial_progress(websocket, job_id)
35
+ await self._handle_message_loop(websocket, job_id)
36
+
37
+ except TimeoutError:
38
+ await self._handle_timeout_error(websocket, job_id)
39
+ except WebSocketDisconnect:
40
+ console.print(f"[yellow]WebSocket disconnected for job: {job_id}[/yellow]")
41
+ except Exception as e:
42
+ await self._handle_connection_error(websocket, job_id, e)
43
+ finally:
44
+ await self._cleanup_connection(job_id, websocket)
45
+
46
+ async def _establish_connection(self, websocket: WebSocket, job_id: str) -> None:
47
+ """Establish WebSocket connection and add to job manager."""
21
48
  await websocket.accept()
22
49
  self.job_manager.add_connection(job_id, websocket)
23
-
24
50
  console.print(f"[green]WebSocket connected for job: {job_id}[/green]")
25
51
 
52
+ async def _send_initial_progress(self, websocket: WebSocket, job_id: str) -> None:
53
+ """Send initial progress data to the connected WebSocket."""
26
54
  try:
27
- initial_progress = self.job_manager.get_job_progress(job_id)
28
- if initial_progress:
29
- await websocket.send_json(initial_progress)
30
- else:
31
- await websocket.send_json(
32
- {
33
- "job_id": job_id,
34
- "status": "waiting",
35
- "message": "Waiting for job to start...",
36
- "overall_progress": 0,
37
- "iteration": 0,
38
- "max_iterations": 10,
39
- "current_stage": "Initializing",
40
- },
55
+ async with self.timeout_manager.timeout_context(
56
+ "websocket_broadcast",
57
+ timeout=5.0,
58
+ strategy=TimeoutStrategy.FAIL_FAST,
59
+ ):
60
+ initial_progress = self.job_manager.get_job_progress(job_id)
61
+ if initial_progress:
62
+ await websocket.send_json(initial_progress)
63
+ else:
64
+ await websocket.send_json(
65
+ self._create_initial_progress_message(job_id)
66
+ )
67
+ except Exception as e:
68
+ console.print(
69
+ f"[red]Failed to send initial progress for {job_id}: {e}[/red]"
70
+ )
71
+
72
+ def _create_initial_progress_message(self, job_id: str) -> dict:
73
+ """Create initial progress message for new jobs."""
74
+ return {
75
+ "job_id": job_id,
76
+ "status": "waiting",
77
+ "message": "Waiting for job to start...",
78
+ "overall_progress": 0,
79
+ "iteration": 0,
80
+ "max_iterations": 10,
81
+ "current_stage": "Initializing",
82
+ }
83
+
84
+ async def _handle_message_loop(self, websocket: WebSocket, job_id: str) -> None:
85
+ """Handle the main message processing loop."""
86
+ message_count = 0
87
+ max_messages = 10000 # Prevent infinite message loops
88
+
89
+ while message_count < max_messages:
90
+ try:
91
+ should_continue = await self._process_single_message(
92
+ websocket, job_id, message_count + 1
93
+ )
94
+ if not should_continue:
95
+ break
96
+ message_count += 1
97
+ except (TimeoutError, WebSocketDisconnect, Exception):
98
+ break
99
+
100
+ if message_count >= max_messages:
101
+ console.print(
102
+ f"[yellow]WebSocket connection limit reached for {job_id}: {max_messages} messages[/yellow]"
103
+ )
104
+
105
+ async def _process_single_message(
106
+ self, websocket: WebSocket, job_id: str, message_count: int
107
+ ) -> bool:
108
+ """Process a single WebSocket message. Returns False to break the loop."""
109
+ try:
110
+ # Add timeout to individual message operations
111
+ async with self.timeout_manager.timeout_context(
112
+ "websocket_message",
113
+ timeout=30.0, # 30 second timeout per message
114
+ strategy=TimeoutStrategy.FAIL_FAST,
115
+ ):
116
+ # Use asyncio.wait_for for additional protection
117
+ data = await asyncio.wait_for(
118
+ websocket.receive_text(),
119
+ timeout=25.0, # Slightly less than timeout context
41
120
  )
42
121
 
43
- while True:
44
- try:
45
- data = await websocket.receive_text()
46
- console.print(
47
- f"[blue]Received message for {job_id}: {data[:100]}...[/blue]",
48
- )
122
+ console.print(
123
+ f"[blue]Received message {message_count} for {job_id}: {data[:100]}...[/blue]",
124
+ )
49
125
 
50
- await websocket.send_json(
126
+ # Respond with timeout protection
127
+ await asyncio.wait_for(
128
+ websocket.send_json(
51
129
  {
52
130
  "type": "echo",
53
131
  "message": f"Received: {data}",
54
132
  "job_id": job_id,
55
- },
56
- )
133
+ "message_count": message_count,
134
+ }
135
+ ),
136
+ timeout=5.0,
137
+ )
57
138
 
58
- except WebSocketDisconnect:
59
- break
139
+ return True
60
140
 
141
+ except TimeoutError:
142
+ console.print(
143
+ f"[yellow]Message timeout for {job_id} after {message_count} messages[/yellow]"
144
+ )
145
+ return False
61
146
  except WebSocketDisconnect:
62
147
  console.print(f"[yellow]WebSocket disconnected for job: {job_id}[/yellow]")
148
+ return False
63
149
  except Exception as e:
64
- console.print(f"[red]WebSocket error for job {job_id}: {e}[/red]")
65
- finally:
150
+ console.print(f"[red]WebSocket message error for job {job_id}: {e}[/red]")
151
+ return False
152
+
153
+ async def _handle_timeout_error(self, websocket: WebSocket, job_id: str) -> None:
154
+ """Handle timeout errors during connection."""
155
+ console.print(
156
+ f"[yellow]WebSocket connection timeout for job: {job_id}[/yellow]"
157
+ )
158
+ with suppress(Exception):
159
+ await websocket.close(code=1001, reason="Connection timeout")
160
+
161
+ async def _handle_connection_error(
162
+ self, websocket: WebSocket, job_id: str, error: Exception
163
+ ) -> None:
164
+ """Handle connection errors."""
165
+ console.print(f"[red]WebSocket error for job {job_id}: {error}[/red]")
166
+ with suppress(Exception):
167
+ await websocket.close(code=1011, reason="Internal error")
168
+
169
+ async def _cleanup_connection(self, job_id: str, websocket: WebSocket) -> None:
170
+ """Clean up the connection."""
171
+ try:
66
172
  self.job_manager.remove_connection(job_id, websocket)
173
+ except Exception as e:
174
+ console.print(f"[red]Error removing connection for {job_id}: {e}[/red]")
67
175
 
68
176
 
69
177
  def register_websocket_routes(
@@ -73,6 +181,6 @@ def register_websocket_routes(
73
181
  ) -> None:
74
182
  handler = WebSocketHandler(job_manager, progress_dir)
75
183
 
76
- @app.websocket(" / ws / progress / {job_id}")
184
+ @app.websocket("/ws/progress/{job_id}")
77
185
  async def websocket_progress_endpoint(websocket: WebSocket, job_id: str) -> None:
78
186
  await handler.handle_connection(websocket, job_id)
@@ -1,8 +1,3 @@
1
- """Adapter for converting between the old OptionsProtocol and new WorkflowOptions.
2
-
3
- This provides backward compatibility during the migration period.
4
- """
5
-
6
1
  import typing as t
7
2
 
8
3
  from .config import (
@@ -21,11 +16,8 @@ from .protocols import OptionsProtocol
21
16
 
22
17
 
23
18
  class OptionsAdapter:
24
- """Adapter to convert between old and new configuration formats."""
25
-
26
19
  @staticmethod
27
20
  def from_options_protocol(options: OptionsProtocol) -> WorkflowOptions:
28
- """Convert OptionsProtocol to WorkflowOptions."""
29
21
  return WorkflowOptions(
30
22
  cleaning=CleaningConfig(
31
23
  clean=getattr(options, "clean", True),
@@ -60,7 +52,7 @@ class OptionsAdapter:
60
52
  ),
61
53
  ai=AIConfig(
62
54
  ai_agent=getattr(options, "ai_agent", False),
63
- autofix=getattr(options, "autofix", True), # Default true per CLAUDE.md
55
+ autofix=getattr(options, "autofix", True),
64
56
  ai_agent_autofix=getattr(options, "ai_agent_autofix", False),
65
57
  start_mcp_server=getattr(options, "start_mcp_server", False),
66
58
  max_iterations=getattr(options, "max_iterations", 10),
@@ -85,18 +77,13 @@ class OptionsAdapter:
85
77
  def to_options_protocol(
86
78
  workflow_options: WorkflowOptions,
87
79
  ) -> "LegacyOptionsWrapper":
88
- """Convert WorkflowOptions back to OptionsProtocol for backward compatibility."""
89
80
  return LegacyOptionsWrapper(workflow_options)
90
81
 
91
82
 
92
83
  class LegacyOptionsWrapper:
93
- """Wrapper that provides OptionsProtocol interface over WorkflowOptions."""
94
-
95
84
  def __init__(self, workflow_options: WorkflowOptions) -> None:
96
85
  self._options = workflow_options
97
86
 
98
- # All the original OptionsProtocol attributes, delegating to the focused configs
99
-
100
87
  @property
101
88
  def commit(self) -> bool:
102
89
  return self._options.git.commit
@@ -223,8 +210,8 @@ class LegacyOptionsWrapper:
223
210
 
224
211
  @property
225
212
  def enterprise_batch(self) -> str | None:
226
- return None # Deprecated field
213
+ return None
227
214
 
228
215
  @property
229
216
  def monitor_dashboard(self) -> str | None:
230
- return None # Deprecated field
217
+ return None
@@ -1,5 +1,6 @@
1
1
  import subprocess
2
2
  import typing as t
3
+ from pathlib import Path
3
4
 
4
5
 
5
6
  @t.runtime_checkable
@@ -45,7 +46,11 @@ class OptionsProtocol(t.Protocol):
45
46
  comp: bool = False
46
47
  enterprise_batch: str | None = None
47
48
  monitor_dashboard: str | None = None
48
- coverage: bool = False
49
+ skip_config_merge: bool = False
50
+ disable_global_locks: bool = False
51
+ global_lock_timeout: int = 600
52
+ global_lock_cleanup: bool = True
53
+ global_lock_dir: str | None = None
49
54
 
50
55
 
51
56
  @t.runtime_checkable
@@ -98,11 +103,11 @@ class HookManager(t.Protocol):
98
103
  class TestManagerProtocol(t.Protocol):
99
104
  def run_tests(self, options: OptionsProtocol) -> bool: ...
100
105
 
101
- def get_coverage(self) -> dict[str, t.Any]: ...
106
+ def get_test_failures(self) -> list[str]: ...
102
107
 
103
108
  def validate_test_environment(self) -> bool: ...
104
109
 
105
- def get_test_failures(self) -> list[str]: ...
110
+ def get_coverage(self) -> dict[str, t.Any]: ...
106
111
 
107
112
 
108
113
  @t.runtime_checkable
@@ -116,3 +121,157 @@ class PublishManager(t.Protocol):
116
121
  def create_git_tag(self, version: str) -> bool: ...
117
122
 
118
123
  def cleanup_old_releases(self, keep_releases: int) -> None: ...
124
+
125
+
126
+ @t.runtime_checkable
127
+ class ConfigMergeServiceProtocol(t.Protocol):
128
+ """Protocol for smart configuration file merging."""
129
+
130
+ def smart_merge_pyproject(
131
+ self,
132
+ source_content: dict[str, t.Any],
133
+ target_path: str | t.Any,
134
+ project_name: str,
135
+ ) -> dict[str, t.Any]: ...
136
+
137
+ def smart_merge_pre_commit_config(
138
+ self,
139
+ source_content: dict[str, t.Any],
140
+ target_path: str | t.Any,
141
+ project_name: str,
142
+ ) -> dict[str, t.Any]: ...
143
+
144
+ def smart_append_file(
145
+ self,
146
+ source_content: str,
147
+ target_path: str | t.Any,
148
+ start_marker: str,
149
+ end_marker: str,
150
+ force: bool = False,
151
+ ) -> str: ...
152
+
153
+ def smart_merge_gitignore(
154
+ self,
155
+ patterns: list[str],
156
+ target_path: str | t.Any,
157
+ ) -> str: ...
158
+
159
+ def write_pyproject_config(
160
+ self,
161
+ config: dict[str, t.Any],
162
+ target_path: str | t.Any,
163
+ ) -> None: ...
164
+
165
+ def write_pre_commit_config(
166
+ self,
167
+ config: dict[str, t.Any],
168
+ target_path: str | t.Any,
169
+ ) -> None: ...
170
+
171
+
172
+ @t.runtime_checkable
173
+ class HookLockManagerProtocol(t.Protocol):
174
+ """Protocol for managing hook-specific locks to prevent concurrent execution."""
175
+
176
+ def requires_lock(self, hook_name: str) -> bool:
177
+ """Check if a hook requires sequential execution.
178
+
179
+ Args:
180
+ hook_name: Name of the hook to check
181
+
182
+ Returns:
183
+ True if the hook requires a lock for sequential execution
184
+ """
185
+ ...
186
+
187
+ async def acquire_hook_lock(self, hook_name: str) -> t.AsyncContextManager[None]:
188
+ """Acquire a lock for the specified hook if it requires one.
189
+
190
+ Args:
191
+ hook_name: Name of the hook to lock
192
+
193
+ Returns:
194
+ Async context manager for lock acquisition
195
+ """
196
+ ...
197
+
198
+ def get_lock_stats(self) -> dict[str, t.Any]:
199
+ """Get statistics about lock usage for monitoring.
200
+
201
+ Returns:
202
+ Dict containing lock statistics per hook
203
+ """
204
+ ...
205
+
206
+ def add_hook_to_lock_list(self, hook_name: str) -> None:
207
+ """Add a hook to the list requiring sequential execution.
208
+
209
+ Args:
210
+ hook_name: Name of the hook to add
211
+ """
212
+ ...
213
+
214
+ def remove_hook_from_lock_list(self, hook_name: str) -> None:
215
+ """Remove a hook from the list requiring sequential execution.
216
+
217
+ Args:
218
+ hook_name: Name of the hook to remove
219
+ """
220
+ ...
221
+
222
+ def is_hook_currently_locked(self, hook_name: str) -> bool:
223
+ """Check if a hook is currently locked.
224
+
225
+ Args:
226
+ hook_name: Name of the hook to check
227
+
228
+ Returns:
229
+ True if the hook is currently locked
230
+ """
231
+ ...
232
+
233
+ def enable_global_lock(self, enabled: bool = True) -> None:
234
+ """Enable or disable global lock functionality.
235
+
236
+ Args:
237
+ enabled: Whether to enable global locking
238
+ """
239
+ ...
240
+
241
+ def is_global_lock_enabled(self) -> bool:
242
+ """Check if global lock functionality is enabled.
243
+
244
+ Returns:
245
+ True if global locking is enabled
246
+ """
247
+ ...
248
+
249
+ def get_global_lock_path(self, hook_name: str) -> Path:
250
+ """Get the filesystem path for a hook's global lock file.
251
+
252
+ Args:
253
+ hook_name: Name of the hook
254
+
255
+ Returns:
256
+ Path to the lock file for the hook
257
+ """
258
+ ...
259
+
260
+ def cleanup_stale_locks(self, max_age_hours: float = 2.0) -> int:
261
+ """Clean up stale lock files older than max_age_hours.
262
+
263
+ Args:
264
+ max_age_hours: Maximum age in hours before a lock is considered stale
265
+
266
+ Returns:
267
+ Number of stale locks cleaned up
268
+ """
269
+ ...
270
+
271
+ def get_global_lock_stats(self) -> dict[str, t.Any]:
272
+ """Get comprehensive statistics about global lock usage.
273
+
274
+ Returns:
275
+ Dictionary containing global lock statistics and metrics
276
+ """
277
+ ...