crackerjack 0.33.0__py3-none-any.whl → 0.33.2__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 (198) hide show
  1. crackerjack/__main__.py +1350 -34
  2. crackerjack/adapters/__init__.py +17 -0
  3. crackerjack/adapters/lsp_client.py +358 -0
  4. crackerjack/adapters/rust_tool_adapter.py +194 -0
  5. crackerjack/adapters/rust_tool_manager.py +193 -0
  6. crackerjack/adapters/skylos_adapter.py +231 -0
  7. crackerjack/adapters/zuban_adapter.py +560 -0
  8. crackerjack/agents/base.py +7 -3
  9. crackerjack/agents/coordinator.py +271 -33
  10. crackerjack/agents/documentation_agent.py +9 -15
  11. crackerjack/agents/dry_agent.py +3 -15
  12. crackerjack/agents/formatting_agent.py +1 -1
  13. crackerjack/agents/import_optimization_agent.py +36 -180
  14. crackerjack/agents/performance_agent.py +17 -98
  15. crackerjack/agents/performance_helpers.py +7 -31
  16. crackerjack/agents/proactive_agent.py +1 -3
  17. crackerjack/agents/refactoring_agent.py +16 -85
  18. crackerjack/agents/refactoring_helpers.py +7 -42
  19. crackerjack/agents/security_agent.py +9 -48
  20. crackerjack/agents/test_creation_agent.py +356 -513
  21. crackerjack/agents/test_specialist_agent.py +0 -4
  22. crackerjack/api.py +6 -25
  23. crackerjack/cli/cache_handlers.py +204 -0
  24. crackerjack/cli/cache_handlers_enhanced.py +683 -0
  25. crackerjack/cli/facade.py +100 -0
  26. crackerjack/cli/handlers.py +224 -9
  27. crackerjack/cli/interactive.py +6 -4
  28. crackerjack/cli/options.py +642 -55
  29. crackerjack/cli/utils.py +2 -1
  30. crackerjack/code_cleaner.py +58 -117
  31. crackerjack/config/global_lock_config.py +8 -48
  32. crackerjack/config/hooks.py +53 -62
  33. crackerjack/core/async_workflow_orchestrator.py +24 -34
  34. crackerjack/core/autofix_coordinator.py +3 -17
  35. crackerjack/core/enhanced_container.py +4 -13
  36. crackerjack/core/file_lifecycle.py +12 -89
  37. crackerjack/core/performance.py +2 -2
  38. crackerjack/core/performance_monitor.py +15 -55
  39. crackerjack/core/phase_coordinator.py +104 -204
  40. crackerjack/core/resource_manager.py +14 -90
  41. crackerjack/core/service_watchdog.py +62 -95
  42. crackerjack/core/session_coordinator.py +149 -0
  43. crackerjack/core/timeout_manager.py +14 -72
  44. crackerjack/core/websocket_lifecycle.py +13 -78
  45. crackerjack/core/workflow_orchestrator.py +171 -174
  46. crackerjack/docs/INDEX.md +11 -0
  47. crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
  48. crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
  49. crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
  50. crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
  51. crackerjack/docs/generated/api/SERVICES.md +1252 -0
  52. crackerjack/documentation/__init__.py +31 -0
  53. crackerjack/documentation/ai_templates.py +756 -0
  54. crackerjack/documentation/dual_output_generator.py +765 -0
  55. crackerjack/documentation/mkdocs_integration.py +518 -0
  56. crackerjack/documentation/reference_generator.py +977 -0
  57. crackerjack/dynamic_config.py +55 -50
  58. crackerjack/executors/async_hook_executor.py +10 -15
  59. crackerjack/executors/cached_hook_executor.py +117 -43
  60. crackerjack/executors/hook_executor.py +8 -34
  61. crackerjack/executors/hook_lock_manager.py +26 -183
  62. crackerjack/executors/individual_hook_executor.py +13 -11
  63. crackerjack/executors/lsp_aware_hook_executor.py +270 -0
  64. crackerjack/executors/tool_proxy.py +417 -0
  65. crackerjack/hooks/lsp_hook.py +79 -0
  66. crackerjack/intelligence/adaptive_learning.py +25 -10
  67. crackerjack/intelligence/agent_orchestrator.py +2 -5
  68. crackerjack/intelligence/agent_registry.py +34 -24
  69. crackerjack/intelligence/agent_selector.py +5 -7
  70. crackerjack/interactive.py +17 -6
  71. crackerjack/managers/async_hook_manager.py +0 -1
  72. crackerjack/managers/hook_manager.py +79 -1
  73. crackerjack/managers/publish_manager.py +44 -8
  74. crackerjack/managers/test_command_builder.py +1 -15
  75. crackerjack/managers/test_executor.py +1 -3
  76. crackerjack/managers/test_manager.py +98 -7
  77. crackerjack/managers/test_manager_backup.py +10 -9
  78. crackerjack/mcp/cache.py +2 -2
  79. crackerjack/mcp/client_runner.py +1 -1
  80. crackerjack/mcp/context.py +191 -68
  81. crackerjack/mcp/dashboard.py +7 -5
  82. crackerjack/mcp/enhanced_progress_monitor.py +31 -28
  83. crackerjack/mcp/file_monitor.py +30 -23
  84. crackerjack/mcp/progress_components.py +31 -21
  85. crackerjack/mcp/progress_monitor.py +50 -53
  86. crackerjack/mcp/rate_limiter.py +6 -6
  87. crackerjack/mcp/server_core.py +17 -16
  88. crackerjack/mcp/service_watchdog.py +2 -1
  89. crackerjack/mcp/state.py +4 -7
  90. crackerjack/mcp/task_manager.py +11 -9
  91. crackerjack/mcp/tools/core_tools.py +173 -32
  92. crackerjack/mcp/tools/error_analyzer.py +3 -2
  93. crackerjack/mcp/tools/execution_tools.py +8 -10
  94. crackerjack/mcp/tools/execution_tools_backup.py +42 -30
  95. crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
  96. crackerjack/mcp/tools/intelligence_tools.py +5 -2
  97. crackerjack/mcp/tools/monitoring_tools.py +33 -70
  98. crackerjack/mcp/tools/proactive_tools.py +24 -11
  99. crackerjack/mcp/tools/progress_tools.py +5 -8
  100. crackerjack/mcp/tools/utility_tools.py +20 -14
  101. crackerjack/mcp/tools/workflow_executor.py +62 -40
  102. crackerjack/mcp/websocket/app.py +8 -0
  103. crackerjack/mcp/websocket/endpoints.py +352 -357
  104. crackerjack/mcp/websocket/jobs.py +40 -57
  105. crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
  106. crackerjack/mcp/websocket/server.py +7 -25
  107. crackerjack/mcp/websocket/websocket_handler.py +6 -17
  108. crackerjack/mixins/__init__.py +0 -2
  109. crackerjack/mixins/error_handling.py +1 -70
  110. crackerjack/models/config.py +12 -1
  111. crackerjack/models/config_adapter.py +49 -1
  112. crackerjack/models/protocols.py +122 -122
  113. crackerjack/models/resource_protocols.py +55 -210
  114. crackerjack/monitoring/ai_agent_watchdog.py +13 -13
  115. crackerjack/monitoring/metrics_collector.py +426 -0
  116. crackerjack/monitoring/regression_prevention.py +8 -8
  117. crackerjack/monitoring/websocket_server.py +643 -0
  118. crackerjack/orchestration/advanced_orchestrator.py +11 -6
  119. crackerjack/orchestration/coverage_improvement.py +3 -3
  120. crackerjack/orchestration/execution_strategies.py +26 -6
  121. crackerjack/orchestration/test_progress_streamer.py +8 -5
  122. crackerjack/plugins/base.py +2 -2
  123. crackerjack/plugins/hooks.py +7 -0
  124. crackerjack/plugins/managers.py +11 -8
  125. crackerjack/security/__init__.py +0 -1
  126. crackerjack/security/audit.py +6 -35
  127. crackerjack/services/anomaly_detector.py +392 -0
  128. crackerjack/services/api_extractor.py +615 -0
  129. crackerjack/services/backup_service.py +2 -2
  130. crackerjack/services/bounded_status_operations.py +15 -152
  131. crackerjack/services/cache.py +127 -1
  132. crackerjack/services/changelog_automation.py +395 -0
  133. crackerjack/services/config.py +15 -9
  134. crackerjack/services/config_merge.py +19 -80
  135. crackerjack/services/config_template.py +506 -0
  136. crackerjack/services/contextual_ai_assistant.py +48 -22
  137. crackerjack/services/coverage_badge_service.py +171 -0
  138. crackerjack/services/coverage_ratchet.py +27 -25
  139. crackerjack/services/debug.py +3 -3
  140. crackerjack/services/dependency_analyzer.py +460 -0
  141. crackerjack/services/dependency_monitor.py +14 -11
  142. crackerjack/services/documentation_generator.py +491 -0
  143. crackerjack/services/documentation_service.py +675 -0
  144. crackerjack/services/enhanced_filesystem.py +6 -5
  145. crackerjack/services/enterprise_optimizer.py +865 -0
  146. crackerjack/services/error_pattern_analyzer.py +676 -0
  147. crackerjack/services/file_hasher.py +1 -1
  148. crackerjack/services/git.py +8 -25
  149. crackerjack/services/health_metrics.py +10 -8
  150. crackerjack/services/heatmap_generator.py +735 -0
  151. crackerjack/services/initialization.py +11 -30
  152. crackerjack/services/input_validator.py +5 -97
  153. crackerjack/services/intelligent_commit.py +327 -0
  154. crackerjack/services/log_manager.py +15 -12
  155. crackerjack/services/logging.py +4 -3
  156. crackerjack/services/lsp_client.py +628 -0
  157. crackerjack/services/memory_optimizer.py +19 -87
  158. crackerjack/services/metrics.py +42 -33
  159. crackerjack/services/parallel_executor.py +9 -67
  160. crackerjack/services/pattern_cache.py +1 -1
  161. crackerjack/services/pattern_detector.py +6 -6
  162. crackerjack/services/performance_benchmarks.py +18 -59
  163. crackerjack/services/performance_cache.py +20 -81
  164. crackerjack/services/performance_monitor.py +27 -95
  165. crackerjack/services/predictive_analytics.py +510 -0
  166. crackerjack/services/quality_baseline.py +234 -0
  167. crackerjack/services/quality_baseline_enhanced.py +646 -0
  168. crackerjack/services/quality_intelligence.py +785 -0
  169. crackerjack/services/regex_patterns.py +618 -524
  170. crackerjack/services/regex_utils.py +43 -123
  171. crackerjack/services/secure_path_utils.py +5 -164
  172. crackerjack/services/secure_status_formatter.py +30 -141
  173. crackerjack/services/secure_subprocess.py +11 -92
  174. crackerjack/services/security.py +9 -41
  175. crackerjack/services/security_logger.py +12 -24
  176. crackerjack/services/server_manager.py +124 -16
  177. crackerjack/services/status_authentication.py +16 -159
  178. crackerjack/services/status_security_manager.py +4 -131
  179. crackerjack/services/thread_safe_status_collector.py +19 -125
  180. crackerjack/services/unified_config.py +21 -13
  181. crackerjack/services/validation_rate_limiter.py +5 -54
  182. crackerjack/services/version_analyzer.py +459 -0
  183. crackerjack/services/version_checker.py +1 -1
  184. crackerjack/services/websocket_resource_limiter.py +10 -144
  185. crackerjack/services/zuban_lsp_service.py +390 -0
  186. crackerjack/slash_commands/__init__.py +2 -7
  187. crackerjack/slash_commands/run.md +2 -2
  188. crackerjack/tools/validate_input_validator_patterns.py +14 -40
  189. crackerjack/tools/validate_regex_patterns.py +19 -48
  190. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/METADATA +196 -25
  191. crackerjack-0.33.2.dist-info/RECORD +229 -0
  192. crackerjack/CLAUDE.md +0 -207
  193. crackerjack/RULES.md +0 -380
  194. crackerjack/py313.py +0 -234
  195. crackerjack-0.33.0.dist-info/RECORD +0 -187
  196. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/WHEEL +0 -0
  197. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/entry_points.txt +0 -0
  198. {crackerjack-0.33.0.dist-info → crackerjack-0.33.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,390 @@
1
+ """Zuban Language Server Protocol (LSP) service for real-time type checking."""
2
+
3
+ import asyncio
4
+ import json
5
+ import logging
6
+ import subprocess
7
+ import time
8
+ import typing as t
9
+ from contextlib import suppress
10
+ from pathlib import Path
11
+
12
+ from rich.console import Console
13
+
14
+ from .security_logger import get_security_logger
15
+
16
+ logger = logging.getLogger("crackerjack.zuban_lsp")
17
+
18
+
19
+ class ZubanLSPService:
20
+ """Manages zuban language server lifecycle and communication."""
21
+
22
+ def __init__(
23
+ self,
24
+ port: int = 8677,
25
+ mode: str = "tcp",
26
+ console: Console | None = None,
27
+ ) -> None:
28
+ """Initialize Zuban LSP service.
29
+
30
+ Args:
31
+ port: TCP port for server (default: 8677)
32
+ mode: Transport mode, "tcp" or "stdio" (default: "tcp")
33
+ console: Rich console for output (optional)
34
+ """
35
+ self.port = port
36
+ self.mode = mode
37
+ self.console = console or Console()
38
+ self.process: subprocess.Popen[bytes] | None = None
39
+ self.start_time: float = 0.0
40
+ self.security_logger = get_security_logger()
41
+ self._health_check_failures = 0
42
+ self._max_health_failures = 3
43
+
44
+ @property
45
+ def is_running(self) -> bool:
46
+ """Check if LSP server process is running."""
47
+ return self.process is not None and self.process.poll() is None
48
+
49
+ @property
50
+ def uptime(self) -> float:
51
+ """Get server uptime in seconds."""
52
+ if self.is_running and self.start_time > 0:
53
+ return time.time() - self.start_time
54
+ return 0.0
55
+
56
+ async def start(self) -> bool:
57
+ """Start the zuban LSP server.
58
+
59
+ Returns:
60
+ True if started successfully, False otherwise
61
+ """
62
+ if self.is_running:
63
+ logger.info("Zuban LSP server already running")
64
+ return True
65
+
66
+ try:
67
+ self.console.print("[cyan]🚀 Starting Zuban LSP server...[/cyan]")
68
+
69
+ # Build command based on transport mode
70
+ if self.mode == "tcp":
71
+ # For TCP mode, we'll need to configure zuban to listen on port
72
+ # Currently zuban server only supports stdio, so we use stdio mode
73
+ cmd = ["uv", "run", "zuban", "server"]
74
+ else:
75
+ cmd = ["uv", "run", "zuban", "server"]
76
+
77
+ # Start the process
78
+ self.process = subprocess.Popen(
79
+ cmd,
80
+ stdin=subprocess.PIPE,
81
+ stdout=subprocess.PIPE,
82
+ stderr=subprocess.PIPE,
83
+ cwd=Path.cwd(),
84
+ start_new_session=True,
85
+ )
86
+
87
+ self.start_time = time.time()
88
+ self._health_check_failures = 0
89
+
90
+ # Log the startup
91
+ self.security_logger.log_subprocess_execution(
92
+ command=cmd,
93
+ purpose="zuban_lsp_server_start",
94
+ )
95
+
96
+ # Wait a moment for startup
97
+ await asyncio.sleep(1.0)
98
+
99
+ # Verify it started successfully
100
+ if not self.is_running:
101
+ error_output = ""
102
+ if self.process and self.process.stderr:
103
+ with suppress(Exception):
104
+ error_output = self.process.stderr.read().decode()
105
+
106
+ self.console.print("[red]❌ Failed to start Zuban LSP server[/red]")
107
+ if error_output:
108
+ logger.error(f"Zuban LSP startup error: {error_output}")
109
+ return False
110
+
111
+ self.console.print(
112
+ f"[green]✅ Zuban LSP server started (PID: {self.process.pid})[/green]"
113
+ )
114
+ logger.info(f"Zuban LSP server started with PID {self.process.pid}")
115
+ return True
116
+
117
+ except Exception as e:
118
+ self.console.print(f"[red]❌ Error starting Zuban LSP server: {e}[/red]")
119
+ logger.error(f"Failed to start Zuban LSP server: {e}")
120
+ return False
121
+
122
+ async def stop(self) -> None:
123
+ """Gracefully stop the LSP server."""
124
+ if not self.process:
125
+ return
126
+
127
+ self.console.print("[yellow]🛑 Stopping Zuban LSP server...[/yellow]")
128
+
129
+ try:
130
+ # Try graceful shutdown first
131
+ self.process.terminate()
132
+
133
+ # Wait for graceful shutdown
134
+ try:
135
+ self.process.wait(timeout=5.0)
136
+ self.console.print(
137
+ "[green]✅ Zuban LSP server stopped gracefully[/green]"
138
+ )
139
+ except subprocess.TimeoutExpired:
140
+ # Force kill if graceful shutdown fails
141
+ self.process.kill()
142
+ self.process.wait(timeout=2.0)
143
+ self.console.print("[yellow]⚠️ Zuban LSP server force stopped[/yellow]")
144
+
145
+ except Exception as e:
146
+ logger.error(f"Error stopping Zuban LSP server: {e}")
147
+
148
+ finally:
149
+ self.process = None
150
+ self.start_time = 0.0
151
+ self._health_check_failures = 0
152
+
153
+ async def health_check(self) -> bool:
154
+ """Check if LSP server is responsive.
155
+
156
+ Returns:
157
+ True if server is healthy, False otherwise
158
+ """
159
+ if not self.is_running:
160
+ return False
161
+
162
+ try:
163
+ # For stdio mode, we check if process is alive and responsive
164
+ if self.mode == "stdio":
165
+ return self._check_stdio_health()
166
+ else:
167
+ # For TCP mode, we would check port connectivity
168
+ return self._check_tcp_health()
169
+
170
+ except Exception as e:
171
+ logger.warning(f"Health check failed: {e}")
172
+ self._health_check_failures += 1
173
+ return False
174
+
175
+ def _check_stdio_health(self) -> bool:
176
+ """Check health for stdio mode server."""
177
+ if not self.process:
178
+ return False
179
+
180
+ # Simple check - is process still alive?
181
+ return self.process.poll() is None
182
+
183
+ def _check_tcp_health(self) -> bool:
184
+ """Check health for TCP mode server."""
185
+ # TODO: Implement TCP health check when zuban supports TCP mode
186
+ # For now, fall back to process check
187
+ return self._check_stdio_health()
188
+
189
+ async def restart(self) -> bool:
190
+ """Restart the LSP server.
191
+
192
+ Returns:
193
+ True if restarted successfully, False otherwise
194
+ """
195
+ self.console.print("[cyan]🔄 Restarting Zuban LSP server...[/cyan]")
196
+
197
+ await self.stop()
198
+ await asyncio.sleep(2.0) # Brief pause between stop and start
199
+
200
+ success = await self.start()
201
+ if success:
202
+ self.console.print(
203
+ "[green]✅ Zuban LSP server restarted successfully[/green]"
204
+ )
205
+ else:
206
+ self.console.print("[red]❌ Failed to restart Zuban LSP server[/red]")
207
+
208
+ return success
209
+
210
+ def get_status(self) -> dict[str, t.Any]:
211
+ """Get current status of the LSP server.
212
+
213
+ Returns:
214
+ Dictionary with server status information
215
+ """
216
+ return {
217
+ "running": self.is_running,
218
+ "pid": self.process.pid if self.process else None,
219
+ "uptime": self.uptime,
220
+ "port": self.port,
221
+ "mode": self.mode,
222
+ "health_failures": self._health_check_failures,
223
+ "max_health_failures": self._max_health_failures,
224
+ "healthy": self.is_running
225
+ and self._health_check_failures < self._max_health_failures,
226
+ }
227
+
228
+ async def send_lsp_request(
229
+ self, method: str, params: dict[str, t.Any] | None = None
230
+ ) -> dict[str, t.Any] | None:
231
+ """Send an LSP request to the server.
232
+
233
+ Args:
234
+ method: LSP method name (e.g., "initialize", "textDocument/didOpen")
235
+ params: Request parameters (optional)
236
+
237
+ Returns:
238
+ LSP response or None if failed
239
+ """
240
+ if not self.is_running or not self.process or not self.process.stdin:
241
+ return None
242
+
243
+ try:
244
+ request_id = int(time.time() * 1000)
245
+ request = {
246
+ "jsonrpc": "2.0",
247
+ "id": request_id,
248
+ "method": method,
249
+ }
250
+
251
+ if params is not None:
252
+ request["params"] = params
253
+
254
+ request_json = json.dumps(request)
255
+ content_length = len(request_json.encode())
256
+
257
+ # LSP protocol: Content-Length header + \r\n\r\n + JSON
258
+ message = f"Content-Length: {content_length}\r\n\r\n{request_json}"
259
+
260
+ self.process.stdin.write(message.encode())
261
+ self.process.stdin.flush()
262
+
263
+ # For notifications (no response expected), return success
264
+ if method.startswith("textDocument/did"):
265
+ return {"status": "notification_sent", "id": request_id}
266
+
267
+ # For requests that expect responses, attempt to read response
268
+ try:
269
+ response = await self._read_lsp_response(request_id, timeout=5.0)
270
+ return response
271
+ except TimeoutError:
272
+ logger.warning(f"LSP request {method} timed out")
273
+ return {"status": "timeout", "id": request_id}
274
+
275
+ except Exception as e:
276
+ logger.error(f"Failed to send LSP request: {e}")
277
+ return None
278
+
279
+ async def _read_lsp_response(
280
+ self, expected_id: int, timeout: float = 5.0
281
+ ) -> dict[str, t.Any] | None:
282
+ """Read LSP response from server with timeout.
283
+
284
+ Args:
285
+ expected_id: Expected request ID for the response
286
+ timeout: Timeout in seconds
287
+
288
+ Returns:
289
+ LSP response dictionary or None if failed
290
+ """
291
+ if not self.process or not self.process.stdout:
292
+ return None
293
+
294
+ try:
295
+ # Read with timeout
296
+ response_data = await asyncio.wait_for(
297
+ self._read_message_from_stdout(), timeout=timeout
298
+ )
299
+
300
+ if not response_data:
301
+ return None
302
+
303
+ response = json.loads(response_data)
304
+ typed_response = t.cast(dict[str, t.Any], response)
305
+
306
+ # Check if this is the response we're looking for
307
+ if typed_response.get("id") == expected_id:
308
+ return typed_response
309
+
310
+ # Log if we got a different response
311
+ logger.debug(
312
+ f"Received response for ID {typed_response.get('id')}, expected {expected_id}"
313
+ )
314
+ return typed_response
315
+
316
+ except TimeoutError:
317
+ raise
318
+ except Exception as e:
319
+ logger.error(f"Failed to read LSP response: {e}")
320
+ return None
321
+
322
+ async def _read_message_from_stdout(self) -> str | None:
323
+ """Read a complete LSP message from stdout.
324
+
325
+ Returns:
326
+ Message content as string, or None if failed
327
+ """
328
+ if not self.process or not self.process.stdout:
329
+ return None
330
+
331
+ try:
332
+ # Read the Content-Length header
333
+ header_line = await self._read_line_async()
334
+ if not header_line or not header_line.startswith("Content-Length:"):
335
+ return None
336
+
337
+ # Extract content length
338
+ content_length = int(header_line.split(":", 1)[1].strip())
339
+
340
+ # Read the empty line separator
341
+ empty_line = await self._read_line_async()
342
+ if empty_line.strip(): # Should be empty
343
+ logger.warning("Expected empty line after Content-Length header")
344
+
345
+ # Read the JSON content
346
+ content_bytes = await self._read_bytes_async(content_length)
347
+ return content_bytes.decode("utf-8")
348
+
349
+ except Exception as e:
350
+ logger.error(f"Failed to read LSP message: {e}")
351
+ return None
352
+
353
+ async def _read_line_async(self) -> str:
354
+ """Read a line from stdout asynchronously."""
355
+ if not self.process or not self.process.stdout:
356
+ return ""
357
+
358
+ # This is a simplified implementation
359
+ # In a production system, you'd want to use proper async I/O
360
+ loop = asyncio.get_event_loop()
361
+ line = await loop.run_in_executor(None, self.process.stdout.readline)
362
+ return line.decode("utf-8").rstrip("\r\n")
363
+
364
+ async def _read_bytes_async(self, count: int) -> bytes:
365
+ """Read specified number of bytes from stdout asynchronously."""
366
+ if not self.process or not self.process.stdout:
367
+ return b""
368
+
369
+ loop = asyncio.get_event_loop()
370
+ data = await loop.run_in_executor(None, self.process.stdout.read, count)
371
+ return data
372
+
373
+
374
+ async def create_zuban_lsp_service(
375
+ port: int = 8677,
376
+ mode: str = "tcp",
377
+ console: Console | None = None,
378
+ ) -> ZubanLSPService:
379
+ """Factory function to create and optionally start Zuban LSP service.
380
+
381
+ Args:
382
+ port: TCP port for server (default: 8677)
383
+ mode: Transport mode, "tcp" or "stdio" (default: "tcp")
384
+ console: Rich console for output (optional)
385
+
386
+ Returns:
387
+ Configured ZubanLSPService instance
388
+ """
389
+ service = ZubanLSPService(port=port, mode=mode, console=console)
390
+ return service
@@ -7,16 +7,13 @@ SLASH_COMMANDS_DIR = Path(__file__).parent
7
7
 
8
8
 
9
9
  def get_slash_command_path(command_name: str) -> Path:
10
- """Get path to slash command file with validation."""
11
10
  try:
12
- # Validate command name to prevent directory traversal
13
11
  sanitized_name = validate_and_sanitize_string(
14
12
  command_name, max_length=50, strict_alphanumeric=True
15
13
  )
16
14
 
17
15
  command_path = SLASH_COMMANDS_DIR / f"{sanitized_name}.md"
18
16
 
19
- # Ensure the path stays within the slash commands directory
20
17
  if not str(command_path.resolve()).startswith(
21
18
  str(SLASH_COMMANDS_DIR.resolve())
22
19
  ):
@@ -37,26 +34,24 @@ def get_slash_command_path(command_name: str) -> Path:
37
34
 
38
35
 
39
36
  def list_available_commands() -> list[str]:
40
- """List available slash commands with validation."""
41
37
  try:
42
38
  commands = []
43
39
  for file_path in SLASH_COMMANDS_DIR.glob("*.md"):
44
40
  command_name = file_path.stem
45
- # Validate each command name
41
+
46
42
  try:
47
43
  validate_and_sanitize_string(
48
44
  command_name, max_length=50, strict_alphanumeric=True
49
45
  )
50
46
  commands.append(command_name)
51
47
  except ExecutionError:
52
- # Skip invalid command names
53
48
  continue
54
49
 
55
50
  return sorted(commands)
56
51
 
57
52
  except Exception as e:
58
53
  raise ExecutionError(
59
- message="Failed to list available commands",
54
+ message="Failed to list[t.Any] available commands",
60
55
  error_code=ErrorCode.FILE_READ_ERROR,
61
56
  ) from e
62
57
 
@@ -24,7 +24,7 @@ Run Crackerjack with advanced orchestrated AI-powered auto-fix mode to automatic
24
24
 
25
25
  This slash command runs Crackerjack with AI agent mode for autonomous code quality enforcement:
26
26
 
27
- - `--ai-agent`: AI agent mode for structured error output and intelligent fixing
27
+ - `--ai-fix`: AI auto-fixing mode for structured error output and intelligent fixing
28
28
  - `--test`: Run tests with comprehensive test coverage
29
29
  - `--verbose`: Show detailed AI decision-making and execution details
30
30
 
@@ -104,7 +104,7 @@ AI: I'll use the /crackerjack:run command to automatically fix all code quality
104
104
 
105
105
  /crackerjack:run
106
106
 
107
- [AI runs: python -m crackerjack --ai-agent --test --verbose]
107
+ [AI runs: python -m crackerjack --ai-fix --test --verbose]
108
108
 
109
109
  The crackerjack AI agent completed successfully after 3 iterations! Here's what was automatically fixed:
110
110
 
@@ -1,34 +1,22 @@
1
- #!/usr/bin/env python3
2
- """
3
- Test script to validate that all new input validator SAFE_PATTERNS work correctly.
4
-
5
- This script validates the security-critical input validation patterns and ensures
6
- they provide proper protection against injection attacks and malicious input.
7
- """
8
-
9
1
  import sys
10
2
  from pathlib import Path
11
3
 
12
- # Add the crackerjack package to the path
13
4
  sys.path.insert(0, str(Path(__file__).parent.parent))
14
5
 
15
6
  from crackerjack.services.input_validator import SecureInputValidator
16
7
  from crackerjack.services.regex_patterns import SAFE_PATTERNS
17
8
 
18
9
 
19
- def test_sql_injection_patterns():
20
- """Test SQL injection detection patterns."""
10
+ def test_sql_injection_patterns() -> bool:
21
11
  print("Testing SQL injection patterns...")
22
12
 
23
13
  test_cases = [
24
- # Should be detected as malicious
25
14
  ("SELECT * FROM users", True, "Basic SELECT"),
26
15
  ("UNION SELECT password FROM admin", True, "UNION injection"),
27
16
  ("'; DROP TABLE users; --", True, "SQL comment injection"),
28
17
  ("' OR 1=1--", True, "Boolean injection"),
29
18
  ("xp_cmdshell('dir')", True, "SQL Server specific"),
30
19
  ("sp_executesql @sql", True, "SQL Server procedure"),
31
- # Should be allowed (legitimate text)
32
20
  ("user selected item", False, "Legitimate text with 'select'"),
33
21
  ("button execution", False, "Legitimate text with 'execution'"),
34
22
  ("team membership", False, "Legitimate text without SQL keywords"),
@@ -51,23 +39,21 @@ def test_sql_injection_patterns():
51
39
 
52
40
  status = "✅" if detected == should_detect else "❌"
53
41
  print(
54
- f" {status} {description}: '{text}' -> {'BLOCKED' if detected else 'ALLOWED'}"
42
+ f" {status} {description}: '{text}' -> {'BLOCKED' if detected else 'ALLOWED'}"
55
43
  )
56
44
 
57
45
  if detected != should_detect:
58
- print(f" Expected: {'BLOCKED' if should_detect else 'ALLOWED'}")
46
+ print(f" Expected: {'BLOCKED' if should_detect else 'ALLOWED'}")
59
47
  return False
60
48
 
61
49
  print("✅ All SQL injection pattern tests passed!")
62
50
  return True
63
51
 
64
52
 
65
- def test_code_injection_patterns():
66
- """Test code injection detection patterns."""
53
+ def test_code_injection_patterns() -> bool:
67
54
  print("\nTesting code injection patterns...")
68
55
 
69
56
  test_cases = [
70
- # Should be detected as malicious
71
57
  ("eval(user_input)", True, "eval() execution"),
72
58
  ("exec(malicious_code)", True, "exec() execution"),
73
59
  ("__import__('os')", True, "Dynamic import"),
@@ -75,7 +61,6 @@ def test_code_injection_patterns():
75
61
  ("subprocess.run(cmd)", True, "System command"),
76
62
  ("os.system('rm -rf')", True, "OS system call"),
77
63
  ("compile(code, 'string', 'exec')", True, "Code compilation"),
78
- # Should be allowed (legitimate text)
79
64
  ("evaluate the results", False, "Legitimate text with 'eval'"),
80
65
  ("execute the plan", False, "Legitimate text with 'execute'"),
81
66
  ("import statement", False, "Normal import discussion"),
@@ -98,30 +83,27 @@ def test_code_injection_patterns():
98
83
 
99
84
  status = "✅" if detected == should_detect else "❌"
100
85
  print(
101
- f" {status} {description}: '{text}' -> {'BLOCKED' if detected else 'ALLOWED'}"
86
+ f" {status} {description}: '{text}' -> {'BLOCKED' if detected else 'ALLOWED'}"
102
87
  )
103
88
 
104
89
  if detected != should_detect:
105
- print(f" Expected: {'BLOCKED' if should_detect else 'ALLOWED'}")
90
+ print(f" Expected: {'BLOCKED' if should_detect else 'ALLOWED'}")
106
91
  return False
107
92
 
108
93
  print("✅ All code injection pattern tests passed!")
109
94
  return True
110
95
 
111
96
 
112
- def test_job_id_validation():
113
- """Test job ID format validation."""
97
+ def test_job_id_validation() -> bool:
114
98
  print("\nTesting job ID validation...")
115
99
 
116
100
  test_cases = [
117
- # Should be valid
118
101
  ("valid_job-123", True, "Standard job ID"),
119
102
  ("another-valid_job", True, "Hyphen and underscore"),
120
103
  ("JOB123", True, "Uppercase"),
121
104
  ("job_456", True, "Underscore only"),
122
105
  ("job-789", True, "Hyphen only"),
123
106
  ("complex_job-id_123", True, "Complex valid ID"),
124
- # Should be invalid
125
107
  ("job with spaces", False, "Contains spaces"),
126
108
  ("job@invalid", False, "Contains @ symbol"),
127
109
  ("job.invalid", False, "Contains dot"),
@@ -137,29 +119,26 @@ def test_job_id_validation():
137
119
  is_valid = pattern.test(job_id)
138
120
  status = "✅" if is_valid == should_be_valid else "❌"
139
121
  print(
140
- f" {status} {description}: '{job_id}' -> {'VALID' if is_valid else 'INVALID'}"
122
+ f" {status} {description}: '{job_id}' -> {'VALID' if is_valid else 'INVALID'}"
141
123
  )
142
124
 
143
125
  if is_valid != should_be_valid:
144
- print(f" Expected: {'VALID' if should_be_valid else 'INVALID'}")
126
+ print(f" Expected: {'VALID' if should_be_valid else 'INVALID'}")
145
127
  return False
146
128
 
147
129
  print("✅ All job ID validation tests passed!")
148
130
  return True
149
131
 
150
132
 
151
- def test_env_var_validation():
152
- """Test environment variable name validation."""
133
+ def test_env_var_validation() -> bool:
153
134
  print("\nTesting environment variable name validation...")
154
135
 
155
136
  test_cases = [
156
- # Should be valid
157
137
  ("VALID_VAR", True, "Standard env var"),
158
138
  ("_PRIVATE_VAR", True, "Starting with underscore"),
159
139
  ("API_KEY_123", True, "With numbers"),
160
140
  ("DATABASE_URL", True, "Typical env var"),
161
141
  ("MAX_RETRIES", True, "Another typical var"),
162
- # Should be invalid
163
142
  ("lowercase_var", False, "Contains lowercase"),
164
143
  ("123_INVALID", False, "Starts with number"),
165
144
  ("INVALID-VAR", False, "Contains hyphen"),
@@ -175,31 +154,28 @@ def test_env_var_validation():
175
154
  is_valid = pattern.test(env_var)
176
155
  status = "✅" if is_valid == should_be_valid else "❌"
177
156
  print(
178
- f" {status} {description}: '{env_var}' -> {'VALID' if is_valid else 'INVALID'}"
157
+ f" {status} {description}: '{env_var}' -> {'VALID' if is_valid else 'INVALID'}"
179
158
  )
180
159
 
181
160
  if is_valid != should_be_valid:
182
- print(f" Expected: {'VALID' if should_be_valid else 'INVALID'}")
161
+ print(f" Expected: {'VALID' if should_be_valid else 'INVALID'}")
183
162
  return False
184
163
 
185
164
  print("✅ All environment variable validation tests passed!")
186
165
  return True
187
166
 
188
167
 
189
- def test_integration_with_validator():
190
- """Test integration with SecureInputValidator."""
168
+ def test_integration_with_validator() -> bool:
191
169
  print("\nTesting integration with SecureInputValidator...")
192
170
 
193
171
  validator = SecureInputValidator()
194
172
 
195
- # Test SQL injection detection
196
173
  result = validator.sanitizer.sanitize_string("'; DROP TABLE users; --")
197
174
  if result.valid:
198
175
  print("❌ SQL injection should have been detected")
199
176
  return False
200
177
  print("✅ SQL injection properly detected and blocked")
201
178
 
202
- # Test job ID validation
203
179
  result = validator.validate_job_id("valid_job-123")
204
180
  if not result.valid:
205
181
  print("❌ Valid job ID should have been accepted")
@@ -212,7 +188,6 @@ def test_integration_with_validator():
212
188
  return False
213
189
  print("✅ Invalid job ID properly rejected")
214
190
 
215
- # Test environment variable validation
216
191
  result = validator.validate_environment_var("VALID_VAR", "some_value")
217
192
  if not result.valid:
218
193
  print("❌ Valid env var should have been accepted")
@@ -229,8 +204,7 @@ def test_integration_with_validator():
229
204
  return True
230
205
 
231
206
 
232
- def main():
233
- """Run all validation tests."""
207
+ def main() -> int:
234
208
  print("🔒 Validating Input Validator Security Patterns")
235
209
  print("=" * 50)
236
210