crackerjack 0.30.3__py3-none-any.whl → 0.31.7__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 (156) hide show
  1. crackerjack/CLAUDE.md +1005 -0
  2. crackerjack/RULES.md +380 -0
  3. crackerjack/__init__.py +42 -13
  4. crackerjack/__main__.py +227 -299
  5. crackerjack/agents/__init__.py +41 -0
  6. crackerjack/agents/architect_agent.py +281 -0
  7. crackerjack/agents/base.py +170 -0
  8. crackerjack/agents/coordinator.py +512 -0
  9. crackerjack/agents/documentation_agent.py +498 -0
  10. crackerjack/agents/dry_agent.py +388 -0
  11. crackerjack/agents/formatting_agent.py +245 -0
  12. crackerjack/agents/import_optimization_agent.py +281 -0
  13. crackerjack/agents/performance_agent.py +669 -0
  14. crackerjack/agents/proactive_agent.py +104 -0
  15. crackerjack/agents/refactoring_agent.py +788 -0
  16. crackerjack/agents/security_agent.py +529 -0
  17. crackerjack/agents/test_creation_agent.py +657 -0
  18. crackerjack/agents/test_specialist_agent.py +486 -0
  19. crackerjack/agents/tracker.py +212 -0
  20. crackerjack/api.py +560 -0
  21. crackerjack/cli/__init__.py +24 -0
  22. crackerjack/cli/facade.py +104 -0
  23. crackerjack/cli/handlers.py +267 -0
  24. crackerjack/cli/interactive.py +471 -0
  25. crackerjack/cli/options.py +409 -0
  26. crackerjack/cli/utils.py +18 -0
  27. crackerjack/code_cleaner.py +618 -928
  28. crackerjack/config/__init__.py +19 -0
  29. crackerjack/config/hooks.py +218 -0
  30. crackerjack/core/__init__.py +0 -0
  31. crackerjack/core/async_workflow_orchestrator.py +406 -0
  32. crackerjack/core/autofix_coordinator.py +200 -0
  33. crackerjack/core/container.py +104 -0
  34. crackerjack/core/enhanced_container.py +542 -0
  35. crackerjack/core/performance.py +243 -0
  36. crackerjack/core/phase_coordinator.py +585 -0
  37. crackerjack/core/proactive_workflow.py +316 -0
  38. crackerjack/core/session_coordinator.py +289 -0
  39. crackerjack/core/workflow_orchestrator.py +826 -0
  40. crackerjack/dynamic_config.py +94 -103
  41. crackerjack/errors.py +263 -41
  42. crackerjack/executors/__init__.py +11 -0
  43. crackerjack/executors/async_hook_executor.py +431 -0
  44. crackerjack/executors/cached_hook_executor.py +242 -0
  45. crackerjack/executors/hook_executor.py +345 -0
  46. crackerjack/executors/individual_hook_executor.py +669 -0
  47. crackerjack/intelligence/__init__.py +44 -0
  48. crackerjack/intelligence/adaptive_learning.py +751 -0
  49. crackerjack/intelligence/agent_orchestrator.py +551 -0
  50. crackerjack/intelligence/agent_registry.py +414 -0
  51. crackerjack/intelligence/agent_selector.py +502 -0
  52. crackerjack/intelligence/integration.py +290 -0
  53. crackerjack/interactive.py +576 -315
  54. crackerjack/managers/__init__.py +11 -0
  55. crackerjack/managers/async_hook_manager.py +135 -0
  56. crackerjack/managers/hook_manager.py +137 -0
  57. crackerjack/managers/publish_manager.py +433 -0
  58. crackerjack/managers/test_command_builder.py +151 -0
  59. crackerjack/managers/test_executor.py +443 -0
  60. crackerjack/managers/test_manager.py +258 -0
  61. crackerjack/managers/test_manager_backup.py +1124 -0
  62. crackerjack/managers/test_progress.py +114 -0
  63. crackerjack/mcp/__init__.py +0 -0
  64. crackerjack/mcp/cache.py +336 -0
  65. crackerjack/mcp/client_runner.py +104 -0
  66. crackerjack/mcp/context.py +621 -0
  67. crackerjack/mcp/dashboard.py +636 -0
  68. crackerjack/mcp/enhanced_progress_monitor.py +479 -0
  69. crackerjack/mcp/file_monitor.py +336 -0
  70. crackerjack/mcp/progress_components.py +569 -0
  71. crackerjack/mcp/progress_monitor.py +949 -0
  72. crackerjack/mcp/rate_limiter.py +332 -0
  73. crackerjack/mcp/server.py +22 -0
  74. crackerjack/mcp/server_core.py +244 -0
  75. crackerjack/mcp/service_watchdog.py +501 -0
  76. crackerjack/mcp/state.py +395 -0
  77. crackerjack/mcp/task_manager.py +257 -0
  78. crackerjack/mcp/tools/__init__.py +17 -0
  79. crackerjack/mcp/tools/core_tools.py +249 -0
  80. crackerjack/mcp/tools/error_analyzer.py +308 -0
  81. crackerjack/mcp/tools/execution_tools.py +372 -0
  82. crackerjack/mcp/tools/execution_tools_backup.py +1097 -0
  83. crackerjack/mcp/tools/intelligence_tool_registry.py +80 -0
  84. crackerjack/mcp/tools/intelligence_tools.py +314 -0
  85. crackerjack/mcp/tools/monitoring_tools.py +502 -0
  86. crackerjack/mcp/tools/proactive_tools.py +384 -0
  87. crackerjack/mcp/tools/progress_tools.py +217 -0
  88. crackerjack/mcp/tools/utility_tools.py +341 -0
  89. crackerjack/mcp/tools/workflow_executor.py +565 -0
  90. crackerjack/mcp/websocket/__init__.py +14 -0
  91. crackerjack/mcp/websocket/app.py +39 -0
  92. crackerjack/mcp/websocket/endpoints.py +559 -0
  93. crackerjack/mcp/websocket/jobs.py +253 -0
  94. crackerjack/mcp/websocket/server.py +116 -0
  95. crackerjack/mcp/websocket/websocket_handler.py +78 -0
  96. crackerjack/mcp/websocket_server.py +10 -0
  97. crackerjack/models/__init__.py +31 -0
  98. crackerjack/models/config.py +93 -0
  99. crackerjack/models/config_adapter.py +230 -0
  100. crackerjack/models/protocols.py +118 -0
  101. crackerjack/models/task.py +154 -0
  102. crackerjack/monitoring/ai_agent_watchdog.py +450 -0
  103. crackerjack/monitoring/regression_prevention.py +638 -0
  104. crackerjack/orchestration/__init__.py +0 -0
  105. crackerjack/orchestration/advanced_orchestrator.py +970 -0
  106. crackerjack/orchestration/coverage_improvement.py +223 -0
  107. crackerjack/orchestration/execution_strategies.py +341 -0
  108. crackerjack/orchestration/test_progress_streamer.py +636 -0
  109. crackerjack/plugins/__init__.py +15 -0
  110. crackerjack/plugins/base.py +200 -0
  111. crackerjack/plugins/hooks.py +246 -0
  112. crackerjack/plugins/loader.py +335 -0
  113. crackerjack/plugins/managers.py +259 -0
  114. crackerjack/py313.py +8 -3
  115. crackerjack/services/__init__.py +22 -0
  116. crackerjack/services/cache.py +314 -0
  117. crackerjack/services/config.py +358 -0
  118. crackerjack/services/config_integrity.py +99 -0
  119. crackerjack/services/contextual_ai_assistant.py +516 -0
  120. crackerjack/services/coverage_ratchet.py +356 -0
  121. crackerjack/services/debug.py +736 -0
  122. crackerjack/services/dependency_monitor.py +617 -0
  123. crackerjack/services/enhanced_filesystem.py +439 -0
  124. crackerjack/services/file_hasher.py +151 -0
  125. crackerjack/services/filesystem.py +421 -0
  126. crackerjack/services/git.py +176 -0
  127. crackerjack/services/health_metrics.py +611 -0
  128. crackerjack/services/initialization.py +873 -0
  129. crackerjack/services/log_manager.py +286 -0
  130. crackerjack/services/logging.py +174 -0
  131. crackerjack/services/metrics.py +578 -0
  132. crackerjack/services/pattern_cache.py +362 -0
  133. crackerjack/services/pattern_detector.py +515 -0
  134. crackerjack/services/performance_benchmarks.py +653 -0
  135. crackerjack/services/security.py +163 -0
  136. crackerjack/services/server_manager.py +234 -0
  137. crackerjack/services/smart_scheduling.py +144 -0
  138. crackerjack/services/tool_version_service.py +61 -0
  139. crackerjack/services/unified_config.py +437 -0
  140. crackerjack/services/version_checker.py +248 -0
  141. crackerjack/slash_commands/__init__.py +14 -0
  142. crackerjack/slash_commands/init.md +122 -0
  143. crackerjack/slash_commands/run.md +163 -0
  144. crackerjack/slash_commands/status.md +127 -0
  145. crackerjack-0.31.7.dist-info/METADATA +742 -0
  146. crackerjack-0.31.7.dist-info/RECORD +149 -0
  147. crackerjack-0.31.7.dist-info/entry_points.txt +2 -0
  148. crackerjack/.gitignore +0 -34
  149. crackerjack/.libcst.codemod.yaml +0 -18
  150. crackerjack/.pdm.toml +0 -1
  151. crackerjack/crackerjack.py +0 -3805
  152. crackerjack/pyproject.toml +0 -286
  153. crackerjack-0.30.3.dist-info/METADATA +0 -1290
  154. crackerjack-0.30.3.dist-info/RECORD +0 -16
  155. {crackerjack-0.30.3.dist-info → crackerjack-0.31.7.dist-info}/WHEEL +0 -0
  156. {crackerjack-0.30.3.dist-info → crackerjack-0.31.7.dist-info}/licenses/LICENSE +0 -0
@@ -36,13 +36,13 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
36
36
  "id": "trailing-whitespace",
37
37
  "name": "trailing-whitespace",
38
38
  "repo": "https://github.com/pre-commit/pre-commit-hooks",
39
- "rev": "v5.0.0",
39
+ "rev": "v6.0.0",
40
40
  "tier": 1,
41
41
  "time_estimate": 0.2,
42
42
  "stages": None,
43
43
  "args": None,
44
44
  "files": None,
45
- "exclude": None,
45
+ "exclude": r"^\.venv/",
46
46
  "additional_dependencies": None,
47
47
  "types_or": None,
48
48
  "language": None,
@@ -53,13 +53,13 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
53
53
  "id": "end-of-file-fixer",
54
54
  "name": "end-of-file-fixer",
55
55
  "repo": "https://github.com/pre-commit/pre-commit-hooks",
56
- "rev": "v5.0.0",
56
+ "rev": "v6.0.0",
57
57
  "tier": 1,
58
58
  "time_estimate": 0.2,
59
59
  "stages": None,
60
60
  "args": None,
61
61
  "files": None,
62
- "exclude": None,
62
+ "exclude": r"^\.venv/",
63
63
  "additional_dependencies": None,
64
64
  "types_or": None,
65
65
  "language": None,
@@ -70,13 +70,13 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
70
70
  "id": "check-yaml",
71
71
  "name": "check-yaml",
72
72
  "repo": "https://github.com/pre-commit/pre-commit-hooks",
73
- "rev": "v5.0.0",
73
+ "rev": "v6.0.0",
74
74
  "tier": 1,
75
75
  "time_estimate": 0.3,
76
76
  "stages": None,
77
77
  "args": None,
78
78
  "files": None,
79
- "exclude": None,
79
+ "exclude": r"^\.venv/",
80
80
  "additional_dependencies": None,
81
81
  "types_or": None,
82
82
  "language": None,
@@ -87,13 +87,13 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
87
87
  "id": "check-toml",
88
88
  "name": "check-toml",
89
89
  "repo": "https://github.com/pre-commit/pre-commit-hooks",
90
- "rev": "v5.0.0",
90
+ "rev": "v6.0.0",
91
91
  "tier": 1,
92
92
  "time_estimate": 0.3,
93
93
  "stages": None,
94
94
  "args": None,
95
95
  "files": None,
96
- "exclude": None,
96
+ "exclude": r"^\.venv/",
97
97
  "additional_dependencies": None,
98
98
  "types_or": None,
99
99
  "language": None,
@@ -104,13 +104,13 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
104
104
  "id": "check-added-large-files",
105
105
  "name": "check-added-large-files",
106
106
  "repo": "https://github.com/pre-commit/pre-commit-hooks",
107
- "rev": "v5.0.0",
107
+ "rev": "v6.0.0",
108
108
  "tier": 1,
109
109
  "time_estimate": 0.5,
110
110
  "stages": None,
111
111
  "args": None,
112
112
  "files": None,
113
- "exclude": None,
113
+ "exclude": r"^\.venv/",
114
114
  "additional_dependencies": None,
115
115
  "types_or": None,
116
116
  "language": None,
@@ -119,33 +119,16 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
119
119
  },
120
120
  ],
121
121
  "package_management": [
122
- {
123
- "id": "pyproject-fmt",
124
- "name": None,
125
- "repo": "https://github.com/tox-dev/pyproject-fmt",
126
- "rev": "v2.6.0",
127
- "tier": 1,
128
- "time_estimate": 0.5,
129
- "stages": None,
130
- "args": ["-n"],
131
- "files": None,
132
- "exclude": None,
133
- "additional_dependencies": None,
134
- "types_or": None,
135
- "language": None,
136
- "entry": None,
137
- "experimental": False,
138
- },
139
122
  {
140
123
  "id": "uv-lock",
141
124
  "name": None,
142
125
  "repo": "https://github.com/astral-sh/uv-pre-commit",
143
- "rev": "0.7.21",
126
+ "rev": "0.8.14",
144
127
  "tier": 1,
145
128
  "time_estimate": 0.5,
146
129
  "stages": None,
147
130
  "args": None,
148
- "files": "^pyproject\\.toml$",
131
+ "files": r"^pyproject\.toml$",
149
132
  "exclude": None,
150
133
  "additional_dependencies": None,
151
134
  "types_or": None,
@@ -156,16 +139,16 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
156
139
  ],
157
140
  "security": [
158
141
  {
159
- "id": "detect-secrets",
142
+ "id": "gitleaks",
160
143
  "name": None,
161
- "repo": "https://github.com/Yelp/detect-secrets",
162
- "rev": "v1.5.0",
144
+ "repo": "https://github.com/gitleaks/gitleaks",
145
+ "rev": "v8.28.0",
163
146
  "tier": 2,
164
147
  "time_estimate": 1.0,
165
148
  "stages": None,
166
149
  "args": None,
167
150
  "files": None,
168
- "exclude": "uv\\.lock|pyproject\\.toml|tests/.*|docs/.*|.*\\.md",
151
+ "exclude": r"uv\.lock|pyproject\.toml|tests/.*|docs/.*|.*\.md",
169
152
  "additional_dependencies": None,
170
153
  "types_or": None,
171
154
  "language": None,
@@ -181,7 +164,7 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
181
164
  "time_estimate": 3.0,
182
165
  "stages": ["pre-push", "manual"],
183
166
  "args": ["-c", "pyproject.toml", "-r", "-ll"],
184
- "files": None,
167
+ "files": "^crackerjack/.*\\.py$",
185
168
  "exclude": None,
186
169
  "additional_dependencies": None,
187
170
  "types_or": None,
@@ -201,7 +184,7 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
201
184
  "stages": None,
202
185
  "args": None,
203
186
  "files": None,
204
- "exclude": None,
187
+ "exclude": r"^\.venv/",
205
188
  "additional_dependencies": ["tomli"],
206
189
  "types_or": None,
207
190
  "language": None,
@@ -212,13 +195,13 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
212
195
  "id": "ruff-check",
213
196
  "name": None,
214
197
  "repo": "https://github.com/astral-sh/ruff-pre-commit",
215
- "rev": "v0.12.3",
198
+ "rev": "v0.12.11",
216
199
  "tier": 2,
217
200
  "time_estimate": 1.5,
218
201
  "stages": None,
219
202
  "args": None,
220
203
  "files": None,
221
- "exclude": None,
204
+ "exclude": r"^\.venv/",
222
205
  "additional_dependencies": None,
223
206
  "types_or": None,
224
207
  "language": None,
@@ -229,13 +212,13 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
229
212
  "id": "ruff-format",
230
213
  "name": None,
231
214
  "repo": "https://github.com/astral-sh/ruff-pre-commit",
232
- "rev": "v0.12.3",
215
+ "rev": "v0.12.11",
233
216
  "tier": 2,
234
217
  "time_estimate": 1.0,
235
218
  "stages": None,
236
219
  "args": None,
237
220
  "files": None,
238
- "exclude": None,
221
+ "exclude": r"^\.venv/",
239
222
  "additional_dependencies": None,
240
223
  "types_or": None,
241
224
  "language": None,
@@ -252,7 +235,7 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
252
235
  "stages": None,
253
236
  "args": None,
254
237
  "files": None,
255
- "exclude": None,
238
+ "exclude": r"^\.venv/",
256
239
  "additional_dependencies": ["mdformat-ruff"],
257
240
  "types_or": None,
258
241
  "language": None,
@@ -270,7 +253,7 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
270
253
  "time_estimate": 2.0,
271
254
  "stages": ["pre-push", "manual"],
272
255
  "args": None,
273
- "files": None,
256
+ "files": "^crackerjack/.*\\.py$",
274
257
  "exclude": None,
275
258
  "additional_dependencies": None,
276
259
  "types_or": None,
@@ -282,13 +265,13 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
282
265
  "id": "creosote",
283
266
  "name": None,
284
267
  "repo": "https://github.com/fredrikaverpil/creosote",
285
- "rev": "v4.0.3",
268
+ "rev": "v4.1.0",
286
269
  "tier": 3,
287
270
  "time_estimate": 1.5,
288
271
  "stages": ["pre-push", "manual"],
289
272
  "args": None,
290
273
  "files": None,
291
- "exclude": None,
274
+ "exclude": r"^\.venv/",
292
275
  "additional_dependencies": None,
293
276
  "types_or": None,
294
277
  "language": None,
@@ -299,13 +282,13 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
299
282
  "id": "complexipy",
300
283
  "name": None,
301
284
  "repo": "https://github.com/rohaquinlop/complexipy-pre-commit",
302
- "rev": "v3.0.0",
285
+ "rev": "v3.3.0",
303
286
  "tier": 3,
304
287
  "time_estimate": 2.0,
305
288
  "stages": ["pre-push", "manual"],
306
289
  "args": ["-d", "low"],
307
- "files": None,
308
- "exclude": None,
290
+ "files": r"^crackerjack/.*\.py$",
291
+ "exclude": r"^(\.venv/|tests/)",
309
292
  "additional_dependencies": None,
310
293
  "types_or": None,
311
294
  "language": None,
@@ -320,48 +303,26 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
320
303
  "tier": 3,
321
304
  "time_estimate": 3.0,
322
305
  "stages": ["pre-push", "manual"],
323
- "args": None,
324
- "files": None,
325
- "exclude": None,
306
+ "args": ["--ignore", "FURB184", "--ignore", "FURB120"],
307
+ "files": "^crackerjack/.*\\.py$",
308
+ "exclude": r"^tests/.*\.py$",
326
309
  "additional_dependencies": None,
327
310
  "types_or": None,
328
311
  "language": None,
329
312
  "entry": None,
330
313
  "experimental": False,
331
314
  },
332
- {
333
- "id": "autotyping",
334
- "name": "autotyping",
335
- "repo": "local",
336
- "rev": "",
337
- "tier": 3,
338
- "time_estimate": 7.0,
339
- "stages": ["pre-push", "manual"],
340
- "args": [
341
- "--aggressive",
342
- "--only-without-imports",
343
- "--guess-common-names",
344
- "crackerjack",
345
- ],
346
- "files": "^crackerjack/.*\\.py$",
347
- "exclude": None,
348
- "additional_dependencies": ["autotyping>=24.3.0", "libcst>=1.1.0"],
349
- "types_or": ["python", "pyi"],
350
- "language": "python",
351
- "entry": "python -m autotyping",
352
- "experimental": False,
353
- },
354
315
  {
355
316
  "id": "pyright",
356
317
  "name": None,
357
318
  "repo": "https://github.com/RobertCraigie/pyright-python",
358
- "rev": "v1.1.403",
319
+ "rev": "v1.1.404",
359
320
  "tier": 3,
360
321
  "time_estimate": 5.0,
361
322
  "stages": ["pre-push", "manual"],
362
323
  "args": None,
363
- "files": None,
364
- "exclude": None,
324
+ "files": "^crackerjack/.*\\.py$",
325
+ "exclude": r"^crackerjack/(mcp|plugins)/.*\.py$|crackerjack/code_cleaner\.py$",
365
326
  "additional_dependencies": None,
366
327
  "types_or": None,
367
328
  "language": None,
@@ -381,7 +342,7 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
381
342
  "args": ["--check"],
382
343
  "files": "^crackerjack/.*\\.py$",
383
344
  "exclude": None,
384
- "additional_dependencies": ["pyrefly>=0.1.0"],
345
+ "additional_dependencies": ["pyrefly >= 0.1.0"],
385
346
  "types_or": ["python"],
386
347
  "language": "python",
387
348
  "entry": "python -m pyrefly",
@@ -398,7 +359,7 @@ HOOKS_REGISTRY: dict[str, list[HookMetadata]] = {
398
359
  "args": ["--check"],
399
360
  "files": "^crackerjack/.*\\.py$",
400
361
  "exclude": None,
401
- "additional_dependencies": ["ty>=0.1.0"],
362
+ "additional_dependencies": ["ty >= 0.1.0"],
402
363
  "types_or": ["python"],
403
364
  "language": "python",
404
365
  "entry": "python -m ty",
@@ -429,15 +390,16 @@ CONFIG_MODES: dict[str, ConfigMode] = {
429
390
  }
430
391
 
431
392
  PRE_COMMIT_TEMPLATE = """repos:
432
- {%- for repo_group in repos %}
433
- {%- if repo_group.comment %}
393
+ {%- for repo in repos %}
394
+ {%- if repo.comment %}
395
+ # {{ repo.comment }}
434
396
  {%- endif %}
435
- - repo: {{ repo_group.repo }}
436
- {%- if repo_group.rev %}
437
- rev: {{ repo_group.rev }}
397
+ - repo: {{ repo.repo }}
398
+ {%- if repo.rev %}
399
+ rev: {{ repo.rev }}
438
400
  {%- endif %}
439
401
  hooks:
440
- {%- for hook in repo_group.hooks %}
402
+ {%- for hook in repo.hooks %}
441
403
  - id: {{ hook.id }}
442
404
  {%- if hook.name %}
443
405
  name: {{ hook.name }}
@@ -477,7 +439,10 @@ class DynamicConfigGenerator:
477
439
  self.template = jinja2.Template(PRE_COMMIT_TEMPLATE)
478
440
 
479
441
  def _should_include_hook(
480
- self, hook: HookMetadata, config: ConfigMode, enabled_experimental: list[str]
442
+ self,
443
+ hook: HookMetadata,
444
+ config: ConfigMode,
445
+ enabled_experimental: list[str],
481
446
  ) -> bool:
482
447
  if hook["tier"] not in config["tiers"]:
483
448
  return False
@@ -486,15 +451,15 @@ class DynamicConfigGenerator:
486
451
  return False
487
452
  if enabled_experimental and hook["id"] not in enabled_experimental:
488
453
  return False
489
- if hook["time_estimate"] > config["max_time"]:
490
- return False
491
- return True
454
+ return not hook["time_estimate"] > config["max_time"]
492
455
 
493
456
  def filter_hooks_for_mode(
494
- self, mode: str, enabled_experimental: list[str] | None = None
457
+ self,
458
+ mode: str,
459
+ enabled_experimental: list[str] | None = None,
495
460
  ) -> list[HookMetadata]:
496
461
  config = CONFIG_MODES[mode]
497
- filtered_hooks = []
462
+ filtered_hooks: list[HookMetadata] = []
498
463
  enabled_experimental = enabled_experimental or []
499
464
  for category_hooks in HOOKS_REGISTRY.values():
500
465
  for hook in category_hooks:
@@ -504,7 +469,8 @@ class DynamicConfigGenerator:
504
469
  return filtered_hooks
505
470
 
506
471
  def group_hooks_by_repo(
507
- self, hooks: list[HookMetadata]
472
+ self,
473
+ hooks: list[HookMetadata],
508
474
  ) -> dict[tuple[str, str], list[HookMetadata]]:
509
475
  repo_groups: dict[tuple[str, str], list[HookMetadata]] = {}
510
476
  for hook in hooks:
@@ -516,26 +482,48 @@ class DynamicConfigGenerator:
516
482
  return repo_groups
517
483
 
518
484
  def _get_repo_comment(self, repo_url: str) -> str | None:
519
- if repo_url == "https://github.com/pre-commit/pre-commit-hooks":
520
- return "File structure and format validators"
521
- elif (
522
- "security" in repo_url
523
- or "bandit" in repo_url
524
- or "detect-secrets" in repo_url
525
- ):
485
+ repo_comments = {
486
+ "https://github.com/pre-commit/pre-commit-hooks": "File structure and format validators",
487
+ "local": "Local tools and custom hooks",
488
+ }
489
+ if repo_url in repo_comments:
490
+ return repo_comments[repo_url]
491
+ security_keywords = ["security", "bandit", "gitleaks"]
492
+ if any(keyword in repo_url for keyword in security_keywords):
526
493
  return "Security checks"
527
- elif "ruff" in repo_url or "mdformat" in repo_url or "codespell" in repo_url:
494
+ formatting_keywords = ["ruff", "mdformat", "codespell"]
495
+ if any(keyword in repo_url for keyword in formatting_keywords):
528
496
  return "Code formatting and quality"
529
- elif repo_url == "local":
530
- return "Local tools and custom hooks"
497
+
531
498
  return None
532
499
 
500
+ def _merge_configs(
501
+ self,
502
+ base_config: dict[str, t.Any],
503
+ new_config: dict[str, t.Any],
504
+ ) -> dict[str, t.Any]:
505
+ result = base_config.copy()
506
+
507
+ for key, value in new_config.items():
508
+ if (
509
+ key in result
510
+ and isinstance(result[key], dict)
511
+ and isinstance(value, dict)
512
+ ):
513
+ result[key] = self._merge_configs(result[key], value)
514
+ else:
515
+ result[key] = value
516
+
517
+ return result
518
+
533
519
  def generate_config(
534
- self, mode: str, enabled_experimental: list[str] | None = None
520
+ self,
521
+ mode: str,
522
+ enabled_experimental: list[str] | None = None,
535
523
  ) -> str:
536
524
  filtered_hooks = self.filter_hooks_for_mode(mode, enabled_experimental)
537
525
  repo_groups = self.group_hooks_by_repo(filtered_hooks)
538
- repos = []
526
+ repos: list[dict[str, t.Any]] = []
539
527
  for (repo_url, rev), hooks in repo_groups.items():
540
528
  repo_data = {
541
529
  "repo": repo_url,
@@ -548,7 +536,9 @@ class DynamicConfigGenerator:
548
536
  return self.template.render(repos=repos)
549
537
 
550
538
  def create_temp_config(
551
- self, mode: str, enabled_experimental: list[str] | None = None
539
+ self,
540
+ mode: str,
541
+ enabled_experimental: list[str] | None = None,
552
542
  ) -> Path:
553
543
  config_content = self.generate_config(mode, enabled_experimental)
554
544
  temp_file = tempfile.NamedTemporaryFile(
@@ -566,7 +556,8 @@ class DynamicConfigGenerator:
566
556
 
567
557
 
568
558
  def generate_config_for_mode(
569
- mode: str, enabled_experimental: list[str] | None = None
559
+ mode: str,
560
+ enabled_experimental: list[str] | None = None,
570
561
  ) -> Path:
571
562
  return DynamicConfigGenerator().create_temp_config(mode, enabled_experimental)
572
563