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.

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 +605 -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.1.dist-info}/METADATA +196 -25
  191. crackerjack-0.33.1.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.1.dist-info}/WHEEL +0 -0
  197. {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
  198. {crackerjack-0.33.0.dist-info → crackerjack-0.33.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,9 +1,3 @@
1
- """Performance-optimized caching layer for expensive operations.
2
-
3
- This module provides intelligent caching for git operations, file system checks,
4
- and command results to significantly improve workflow performance.
5
- """
6
-
7
1
  import asyncio
8
2
  import builtins
9
3
  import hashlib
@@ -19,23 +13,19 @@ from crackerjack.services.logging import get_logger
19
13
 
20
14
  @dataclass
21
15
  class CacheEntry:
22
- """Represents a cached value with metadata."""
23
-
24
16
  value: t.Any
25
17
  created_at: datetime
26
18
  access_count: int = 0
27
19
  last_accessed: datetime = field(default_factory=datetime.now)
28
- ttl_seconds: int = 300 # Default 5 minutes
20
+ ttl_seconds: int = 300
29
21
  invalidation_keys: set[str] = field(default_factory=set)
30
22
 
31
23
  def is_expired(self) -> bool:
32
- """Check if cache entry has expired."""
33
- if self.ttl_seconds <= 0: # Permanent cache
24
+ if self.ttl_seconds <= 0:
34
25
  return False
35
26
  return datetime.now() > self.created_at + timedelta(seconds=self.ttl_seconds)
36
27
 
37
28
  def access(self) -> t.Any:
38
- """Mark entry as accessed and return value."""
39
29
  self.access_count += 1
40
30
  self.last_accessed = datetime.now()
41
31
  return self.value
@@ -43,8 +33,6 @@ class CacheEntry:
43
33
 
44
34
  @dataclass
45
35
  class CacheStats:
46
- """Cache performance statistics."""
47
-
48
36
  hits: int = 0
49
37
  misses: int = 0
50
38
  evictions: int = 0
@@ -52,14 +40,11 @@ class CacheStats:
52
40
 
53
41
  @property
54
42
  def hit_ratio(self) -> float:
55
- """Calculate cache hit ratio."""
56
43
  total = self.hits + self.misses
57
44
  return self.hits / total if total > 0 else 0.0
58
45
 
59
46
 
60
47
  class PerformanceCache:
61
- """High-performance async cache with intelligent invalidation."""
62
-
63
48
  def __init__(
64
49
  self,
65
50
  max_memory_mb: int = 50,
@@ -77,17 +62,14 @@ class PerformanceCache:
77
62
  self._cleanup_task: asyncio.Task[None] | None = None
78
63
  self._invalidation_map: dict[str, set[str]] = {}
79
64
 
80
- # Weak reference cache for heavy objects
81
65
  self._weak_cache: WeakValueDictionary[str, t.Any] = WeakValueDictionary()
82
66
 
83
67
  async def start(self) -> None:
84
- """Start background cleanup task."""
85
68
  if self._cleanup_task is None:
86
69
  self._cleanup_task = asyncio.create_task(self._cleanup_loop())
87
70
  self._logger.info("Performance cache started")
88
71
 
89
72
  async def stop(self) -> None:
90
- """Stop background cleanup task."""
91
73
  if self._cleanup_task:
92
74
  self._cleanup_task.cancel()
93
75
  try:
@@ -102,7 +84,6 @@ class PerformanceCache:
102
84
  key: str,
103
85
  default: t.Any = None,
104
86
  ) -> t.Any:
105
- """Get value from cache."""
106
87
  with self._lock:
107
88
  if key not in self._cache:
108
89
  self._stats.misses += 1
@@ -123,7 +104,6 @@ class PerformanceCache:
123
104
  key: str,
124
105
  default: t.Any = None,
125
106
  ) -> t.Any:
126
- """Async version of get for compatibility."""
127
107
  return self.get(key, default)
128
108
 
129
109
  def set(
@@ -133,7 +113,6 @@ class PerformanceCache:
133
113
  ttl_seconds: int | None = None,
134
114
  invalidation_keys: set[str] | None = None,
135
115
  ) -> None:
136
- """Set value in cache."""
137
116
  ttl = ttl_seconds if ttl_seconds is not None else self.default_ttl_seconds
138
117
  inv_keys = invalidation_keys or set()
139
118
 
@@ -147,13 +126,11 @@ class PerformanceCache:
147
126
  with self._lock:
148
127
  self._cache[key] = entry
149
128
 
150
- # Update invalidation map
151
129
  for inv_key in inv_keys:
152
130
  if inv_key not in self._invalidation_map:
153
131
  self._invalidation_map[inv_key] = set()
154
132
  self._invalidation_map[inv_key].add(key)
155
133
 
156
- # Check memory usage and evict if needed
157
134
  self._check_memory_pressure()
158
135
 
159
136
  async def set_async(
@@ -163,11 +140,9 @@ class PerformanceCache:
163
140
  ttl_seconds: int | None = None,
164
141
  invalidation_keys: builtins.set[str] | None = None,
165
142
  ) -> None:
166
- """Async version of set for compatibility."""
167
- self.set(key, value, ttl_seconds, invalidation_keys)
143
+ self.set[t.Any](key, value, ttl_seconds, invalidation_keys)
168
144
 
169
145
  def invalidate(self, invalidation_key: str) -> int:
170
- """Invalidate all entries with given invalidation key."""
171
146
  with self._lock:
172
147
  if invalidation_key not in self._invalidation_map:
173
148
  return 0
@@ -181,7 +156,6 @@ class PerformanceCache:
181
156
  count += 1
182
157
  self._stats.evictions += 1
183
158
 
184
- # Clean up invalidation map
185
159
  del self._invalidation_map[invalidation_key]
186
160
 
187
161
  self._logger.debug(
@@ -190,7 +164,6 @@ class PerformanceCache:
190
164
  return count
191
165
 
192
166
  def clear(self) -> None:
193
- """Clear all cache entries."""
194
167
  with self._lock:
195
168
  count = len(self._cache)
196
169
  self._cache.clear()
@@ -199,7 +172,6 @@ class PerformanceCache:
199
172
  self._logger.info(f"Cleared {count} cache entries")
200
173
 
201
174
  def get_stats(self) -> CacheStats:
202
- """Get cache performance statistics."""
203
175
  with self._lock:
204
176
  stats = CacheStats(
205
177
  hits=self._stats.hits,
@@ -210,38 +182,32 @@ class PerformanceCache:
210
182
  return stats
211
183
 
212
184
  def _estimate_memory_usage(self) -> int:
213
- """Estimate memory usage of cache entries."""
214
- # Simplified estimation - in production would use more sophisticated method
215
185
  total_size = 0
216
186
  for entry in self._cache.values():
217
187
  if isinstance(entry.value, str | bytes):
218
188
  total_size += len(entry.value)
219
189
  elif isinstance(entry.value, list | tuple):
220
- total_size += len(entry.value) * 100 # Rough estimate
190
+ total_size += len(entry.value) * 100
221
191
  elif isinstance(entry.value, dict):
222
- total_size += len(entry.value) * 200 # Rough estimate
192
+ total_size += len(entry.value) * 200
223
193
  else:
224
- total_size += 1000 # Default estimate for other objects
194
+ total_size += 1000
225
195
 
226
196
  return total_size
227
197
 
228
198
  def _check_memory_pressure(self) -> None:
229
- """Check memory usage and evict entries if needed."""
230
199
  if self._estimate_memory_usage() > self.max_memory_bytes:
231
200
  self._evict_lru_entries()
232
201
 
233
202
  def _evict_lru_entries(self) -> None:
234
- """Evict least recently used entries to free memory."""
235
203
  if not self._cache:
236
204
  return
237
205
 
238
- # Sort entries by last access time (oldest first)
239
206
  entries_by_access = sorted(
240
207
  self._cache.items(),
241
208
  key=lambda x: x[1].last_accessed,
242
209
  )
243
210
 
244
- # Remove oldest 25% of entries
245
211
  evict_count = max(1, len(entries_by_access) // 4)
246
212
 
247
213
  for key, _ in entries_by_access[:evict_count]:
@@ -251,7 +217,6 @@ class PerformanceCache:
251
217
  self._logger.debug(f"Evicted {evict_count} LRU cache entries")
252
218
 
253
219
  async def _cleanup_loop(self) -> None:
254
- """Background cleanup loop for expired entries."""
255
220
  while True:
256
221
  try:
257
222
  await asyncio.sleep(self.cleanup_interval_seconds)
@@ -262,7 +227,6 @@ class PerformanceCache:
262
227
  self._logger.error(f"Error in cache cleanup loop: {e}")
263
228
 
264
229
  def _cleanup_expired_entries(self) -> None:
265
- """Clean up expired cache entries."""
266
230
  with self._lock:
267
231
  expired_keys = [
268
232
  key for key, entry in self._cache.items() if entry.is_expired()
@@ -277,14 +241,11 @@ class PerformanceCache:
277
241
 
278
242
 
279
243
  class GitOperationCache:
280
- """Specialized cache for Git operations."""
281
-
282
244
  def __init__(self, cache: PerformanceCache):
283
245
  self.cache = cache
284
246
  self._logger = get_logger("crackerjack.git_cache")
285
247
 
286
248
  def _make_repo_key(self, repo_path: Path, operation: str, params: str = "") -> str:
287
- """Create cache key for git operation."""
288
249
  repo_hash = hashlib.md5(
289
250
  str(repo_path).encode(), usedforsecurity=False
290
251
  ).hexdigest()[:8]
@@ -293,10 +254,9 @@ class GitOperationCache:
293
254
  if params
294
255
  else ""
295
256
  )
296
- return f"git:{repo_hash}:{operation}:{param_hash}"
257
+ return f"git: {repo_hash}: {operation}: {param_hash}"
297
258
 
298
259
  def get_branch_info(self, repo_path: Path) -> t.Any:
299
- """Get cached branch information."""
300
260
  key = self._make_repo_key(repo_path, "branch_info")
301
261
  return self.cache.get(key)
302
262
 
@@ -306,13 +266,11 @@ class GitOperationCache:
306
266
  branch_info: t.Any,
307
267
  ttl_seconds: int = 60,
308
268
  ) -> None:
309
- """Cache branch information."""
310
269
  key = self._make_repo_key(repo_path, "branch_info")
311
- invalidation_keys = {f"git_repo:{repo_path}"}
312
- self.cache.set(key, branch_info, ttl_seconds, invalidation_keys)
270
+ invalidation_keys = {f"git_repo: {repo_path}"}
271
+ self.cache.set[t.Any](key, branch_info, ttl_seconds, invalidation_keys)
313
272
 
314
273
  def get_file_status(self, repo_path: Path) -> t.Any:
315
- """Get cached file status."""
316
274
  key = self._make_repo_key(repo_path, "file_status")
317
275
  return self.cache.get(key)
318
276
 
@@ -322,33 +280,27 @@ class GitOperationCache:
322
280
  file_status: t.Any,
323
281
  ttl_seconds: int = 30,
324
282
  ) -> None:
325
- """Cache file status."""
326
283
  key = self._make_repo_key(repo_path, "file_status")
327
- invalidation_keys = {f"git_repo:{repo_path}", "git_files"}
328
- self.cache.set(key, file_status, ttl_seconds, invalidation_keys)
284
+ invalidation_keys = {f"git_repo: {repo_path}", "git_files"}
285
+ self.cache.set[t.Any](key, file_status, ttl_seconds, invalidation_keys)
329
286
 
330
287
  def invalidate_repo(self, repo_path: Path) -> None:
331
- """Invalidate all cache entries for a repository."""
332
- self.cache.invalidate(f"git_repo:{repo_path}")
288
+ self.cache.invalidate(f"git_repo: {repo_path}")
333
289
  self._logger.debug(f"Invalidated git cache for repo: {repo_path}")
334
290
 
335
291
 
336
292
  class FileSystemCache:
337
- """Specialized cache for file system operations."""
338
-
339
293
  def __init__(self, cache: PerformanceCache):
340
294
  self.cache = cache
341
295
  self._logger = get_logger("crackerjack.filesystem_cache")
342
296
 
343
297
  def _make_file_key(self, file_path: Path, operation: str) -> str:
344
- """Create cache key for file operation."""
345
298
  file_hash = hashlib.md5(
346
299
  str(file_path).encode(), usedforsecurity=False
347
300
  ).hexdigest()[:8]
348
- return f"fs:{file_hash}:{operation}"
301
+ return f"fs: {file_hash}: {operation}"
349
302
 
350
303
  def get_file_stats(self, file_path: Path) -> t.Any:
351
- """Get cached file statistics."""
352
304
  key = self._make_file_key(file_path, "stats")
353
305
  return self.cache.get(key)
354
306
 
@@ -358,39 +310,33 @@ class FileSystemCache:
358
310
  stats: t.Any,
359
311
  ttl_seconds: int = 60,
360
312
  ) -> None:
361
- """Cache file statistics."""
362
313
  key = self._make_file_key(file_path, "stats")
363
- invalidation_keys = {f"file:{file_path}"}
364
- self.cache.set(key, stats, ttl_seconds, invalidation_keys)
314
+ invalidation_keys = {f"file: {file_path}"}
315
+ self.cache.set[t.Any](key, stats, ttl_seconds, invalidation_keys)
365
316
 
366
317
  def invalidate_file(self, file_path: Path) -> None:
367
- """Invalidate cache entries for a specific file."""
368
- self.cache.invalidate(f"file:{file_path}")
318
+ self.cache.invalidate(f"file: {file_path}")
369
319
 
370
320
 
371
321
  class CommandResultCache:
372
- """Specialized cache for command execution results."""
373
-
374
322
  def __init__(self, cache: PerformanceCache):
375
323
  self.cache = cache
376
324
  self._logger = get_logger("crackerjack.command_cache")
377
325
 
378
326
  def _make_command_key(self, command: list[str], cwd: Path | None = None) -> str:
379
- """Create cache key for command execution."""
380
327
  cmd_str = " ".join(command)
381
328
  cwd_str = str(cwd) if cwd else ""
382
- combined = f"{cmd_str}:{cwd_str}"
329
+ combined = f"{cmd_str}: {cwd_str}"
383
330
  cmd_hash = hashlib.md5(combined.encode(), usedforsecurity=False).hexdigest()[
384
331
  :12
385
332
  ]
386
- return f"cmd:{cmd_hash}"
333
+ return f"cmd: {cmd_hash}"
387
334
 
388
335
  def get_command_result(
389
336
  self,
390
337
  command: list[str],
391
338
  cwd: Path | None = None,
392
339
  ) -> t.Any:
393
- """Get cached command result."""
394
340
  key = self._make_command_key(command, cwd)
395
341
  return self.cache.get(key)
396
342
 
@@ -401,26 +347,22 @@ class CommandResultCache:
401
347
  cwd: Path | None = None,
402
348
  ttl_seconds: int = 120,
403
349
  ) -> None:
404
- """Cache command execution result."""
405
350
  key = self._make_command_key(command, cwd)
406
351
  invalidation_keys = {"commands"}
407
352
  if cwd:
408
- invalidation_keys.add(f"cwd:{cwd}")
353
+ invalidation_keys.add(f"cwd: {cwd}")
409
354
 
410
- self.cache.set(key, result, ttl_seconds, invalidation_keys)
355
+ self.cache.set[t.Any](key, result, ttl_seconds, invalidation_keys)
411
356
 
412
357
  def invalidate_commands(self) -> None:
413
- """Invalidate all cached command results."""
414
358
  self.cache.invalidate("commands")
415
359
 
416
360
 
417
- # Global cache instance for the application
418
361
  _global_cache: PerformanceCache | None = None
419
362
  _cache_lock = Lock()
420
363
 
421
364
 
422
365
  def get_performance_cache() -> PerformanceCache:
423
- """Get global performance cache instance."""
424
366
  global _global_cache
425
367
  with _cache_lock:
426
368
  if _global_cache is None:
@@ -429,15 +371,12 @@ def get_performance_cache() -> PerformanceCache:
429
371
 
430
372
 
431
373
  def get_git_cache() -> GitOperationCache:
432
- """Get Git operation cache instance."""
433
374
  return GitOperationCache(get_performance_cache())
434
375
 
435
376
 
436
377
  def get_filesystem_cache() -> FileSystemCache:
437
- """Get file system cache instance."""
438
378
  return FileSystemCache(get_performance_cache())
439
379
 
440
380
 
441
381
  def get_command_cache() -> CommandResultCache:
442
- """Get command result cache instance."""
443
382
  return CommandResultCache(get_performance_cache())