crackerjack 0.31.10__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.
- crackerjack/CLAUDE.md +288 -705
- crackerjack/__main__.py +22 -8
- crackerjack/agents/__init__.py +0 -3
- crackerjack/agents/architect_agent.py +0 -43
- crackerjack/agents/base.py +1 -9
- crackerjack/agents/coordinator.py +2 -148
- crackerjack/agents/documentation_agent.py +109 -81
- crackerjack/agents/dry_agent.py +122 -97
- crackerjack/agents/formatting_agent.py +3 -16
- crackerjack/agents/import_optimization_agent.py +1174 -130
- crackerjack/agents/performance_agent.py +956 -188
- crackerjack/agents/performance_helpers.py +229 -0
- crackerjack/agents/proactive_agent.py +1 -48
- crackerjack/agents/refactoring_agent.py +516 -246
- crackerjack/agents/refactoring_helpers.py +282 -0
- crackerjack/agents/security_agent.py +393 -90
- crackerjack/agents/test_creation_agent.py +1776 -120
- crackerjack/agents/test_specialist_agent.py +59 -15
- crackerjack/agents/tracker.py +0 -102
- crackerjack/api.py +145 -37
- crackerjack/cli/handlers.py +48 -30
- crackerjack/cli/interactive.py +11 -11
- crackerjack/cli/options.py +66 -4
- crackerjack/code_cleaner.py +808 -148
- crackerjack/config/global_lock_config.py +110 -0
- crackerjack/config/hooks.py +43 -64
- crackerjack/core/async_workflow_orchestrator.py +247 -97
- crackerjack/core/autofix_coordinator.py +192 -109
- crackerjack/core/enhanced_container.py +46 -63
- crackerjack/core/file_lifecycle.py +549 -0
- crackerjack/core/performance.py +9 -8
- crackerjack/core/performance_monitor.py +395 -0
- crackerjack/core/phase_coordinator.py +281 -94
- crackerjack/core/proactive_workflow.py +9 -58
- crackerjack/core/resource_manager.py +501 -0
- crackerjack/core/service_watchdog.py +490 -0
- crackerjack/core/session_coordinator.py +4 -8
- crackerjack/core/timeout_manager.py +504 -0
- crackerjack/core/websocket_lifecycle.py +475 -0
- crackerjack/core/workflow_orchestrator.py +343 -209
- crackerjack/dynamic_config.py +47 -6
- crackerjack/errors.py +3 -4
- crackerjack/executors/async_hook_executor.py +63 -13
- crackerjack/executors/cached_hook_executor.py +14 -14
- crackerjack/executors/hook_executor.py +100 -37
- crackerjack/executors/hook_lock_manager.py +856 -0
- crackerjack/executors/individual_hook_executor.py +120 -86
- crackerjack/intelligence/__init__.py +0 -7
- crackerjack/intelligence/adaptive_learning.py +13 -86
- crackerjack/intelligence/agent_orchestrator.py +15 -78
- crackerjack/intelligence/agent_registry.py +12 -59
- crackerjack/intelligence/agent_selector.py +31 -92
- crackerjack/intelligence/integration.py +1 -41
- crackerjack/interactive.py +9 -9
- crackerjack/managers/async_hook_manager.py +25 -8
- crackerjack/managers/hook_manager.py +9 -9
- crackerjack/managers/publish_manager.py +57 -59
- crackerjack/managers/test_command_builder.py +6 -36
- crackerjack/managers/test_executor.py +9 -61
- crackerjack/managers/test_manager.py +17 -63
- crackerjack/managers/test_manager_backup.py +77 -127
- crackerjack/managers/test_progress.py +4 -23
- crackerjack/mcp/cache.py +5 -12
- crackerjack/mcp/client_runner.py +10 -10
- crackerjack/mcp/context.py +64 -6
- crackerjack/mcp/dashboard.py +14 -11
- crackerjack/mcp/enhanced_progress_monitor.py +55 -55
- crackerjack/mcp/file_monitor.py +72 -42
- crackerjack/mcp/progress_components.py +103 -84
- crackerjack/mcp/progress_monitor.py +122 -49
- crackerjack/mcp/rate_limiter.py +12 -12
- crackerjack/mcp/server_core.py +16 -22
- crackerjack/mcp/service_watchdog.py +26 -26
- crackerjack/mcp/state.py +15 -0
- crackerjack/mcp/tools/core_tools.py +95 -39
- crackerjack/mcp/tools/error_analyzer.py +6 -32
- crackerjack/mcp/tools/execution_tools.py +1 -56
- crackerjack/mcp/tools/execution_tools_backup.py +35 -131
- crackerjack/mcp/tools/intelligence_tool_registry.py +0 -36
- crackerjack/mcp/tools/intelligence_tools.py +2 -55
- crackerjack/mcp/tools/monitoring_tools.py +308 -145
- crackerjack/mcp/tools/proactive_tools.py +12 -42
- crackerjack/mcp/tools/progress_tools.py +23 -15
- crackerjack/mcp/tools/utility_tools.py +3 -40
- crackerjack/mcp/tools/workflow_executor.py +40 -60
- crackerjack/mcp/websocket/app.py +0 -3
- crackerjack/mcp/websocket/endpoints.py +206 -268
- crackerjack/mcp/websocket/jobs.py +213 -66
- crackerjack/mcp/websocket/server.py +84 -6
- crackerjack/mcp/websocket/websocket_handler.py +137 -29
- crackerjack/models/config_adapter.py +3 -16
- crackerjack/models/protocols.py +162 -3
- crackerjack/models/resource_protocols.py +454 -0
- crackerjack/models/task.py +3 -3
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +25 -71
- crackerjack/monitoring/regression_prevention.py +28 -87
- crackerjack/orchestration/advanced_orchestrator.py +44 -78
- crackerjack/orchestration/coverage_improvement.py +10 -60
- crackerjack/orchestration/execution_strategies.py +16 -16
- crackerjack/orchestration/test_progress_streamer.py +61 -53
- crackerjack/plugins/base.py +1 -1
- crackerjack/plugins/managers.py +22 -20
- crackerjack/py313.py +65 -21
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +627 -0
- crackerjack/services/cache.py +7 -9
- crackerjack/services/config.py +35 -52
- crackerjack/services/config_integrity.py +5 -16
- crackerjack/services/config_merge.py +542 -0
- crackerjack/services/contextual_ai_assistant.py +17 -19
- crackerjack/services/coverage_ratchet.py +44 -73
- crackerjack/services/debug.py +25 -39
- crackerjack/services/dependency_monitor.py +52 -50
- crackerjack/services/enhanced_filesystem.py +14 -11
- crackerjack/services/file_hasher.py +1 -1
- crackerjack/services/filesystem.py +1 -12
- crackerjack/services/git.py +71 -47
- crackerjack/services/health_metrics.py +31 -27
- crackerjack/services/initialization.py +276 -428
- crackerjack/services/input_validator.py +760 -0
- crackerjack/services/log_manager.py +16 -16
- crackerjack/services/logging.py +7 -6
- crackerjack/services/metrics.py +43 -43
- crackerjack/services/pattern_cache.py +2 -31
- crackerjack/services/pattern_detector.py +26 -63
- crackerjack/services/performance_benchmarks.py +20 -45
- crackerjack/services/regex_patterns.py +2887 -0
- crackerjack/services/regex_utils.py +537 -0
- crackerjack/services/secure_path_utils.py +683 -0
- crackerjack/services/secure_status_formatter.py +534 -0
- crackerjack/services/secure_subprocess.py +605 -0
- crackerjack/services/security.py +47 -10
- crackerjack/services/security_logger.py +492 -0
- crackerjack/services/server_manager.py +109 -50
- crackerjack/services/smart_scheduling.py +8 -25
- crackerjack/services/status_authentication.py +603 -0
- crackerjack/services/status_security_manager.py +442 -0
- crackerjack/services/thread_safe_status_collector.py +546 -0
- crackerjack/services/tool_version_service.py +1 -23
- crackerjack/services/unified_config.py +36 -58
- crackerjack/services/validation_rate_limiter.py +269 -0
- crackerjack/services/version_checker.py +9 -40
- crackerjack/services/websocket_resource_limiter.py +572 -0
- crackerjack/slash_commands/__init__.py +52 -2
- crackerjack/tools/__init__.py +0 -0
- crackerjack/tools/validate_input_validator_patterns.py +262 -0
- crackerjack/tools/validate_regex_patterns.py +198 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.12.dist-info}/METADATA +197 -12
- crackerjack-0.31.12.dist-info/RECORD +178 -0
- crackerjack/cli/facade.py +0 -104
- crackerjack-0.31.10.dist-info/RECORD +0 -149
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.12.dist-info}/WHEEL +0 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.12.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.31.10.dist-info → crackerjack-0.31.12.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import re
|
|
2
1
|
import subprocess
|
|
2
|
+
import typing as t
|
|
3
3
|
from contextlib import suppress
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
+
from ..services.regex_patterns import SAFE_PATTERNS
|
|
7
8
|
from .base import (
|
|
8
9
|
FixResult,
|
|
9
10
|
Issue,
|
|
@@ -14,8 +15,6 @@ from .base import (
|
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class DocumentationAgent(SubAgent):
|
|
17
|
-
"""Agent specialized in maintaining documentation consistency and changelog updates."""
|
|
18
|
-
|
|
19
18
|
def get_supported_types(self) -> set[IssueType]:
|
|
20
19
|
return {IssueType.DOCUMENTATION}
|
|
21
20
|
|
|
@@ -28,7 +27,6 @@ class DocumentationAgent(SubAgent):
|
|
|
28
27
|
self.log(f"Analyzing documentation issue: {issue.message}")
|
|
29
28
|
|
|
30
29
|
try:
|
|
31
|
-
# Detect what type of documentation update is needed
|
|
32
30
|
if "changelog" in issue.message.lower():
|
|
33
31
|
return await self._update_changelog(issue)
|
|
34
32
|
if (
|
|
@@ -48,12 +46,10 @@ class DocumentationAgent(SubAgent):
|
|
|
48
46
|
)
|
|
49
47
|
|
|
50
48
|
async def _update_changelog(self, issue: Issue) -> FixResult:
|
|
51
|
-
"""Update CHANGELOG.md with recent changes."""
|
|
52
49
|
self.log("Updating changelog with recent changes")
|
|
53
50
|
|
|
54
51
|
changelog_path = Path("CHANGELOG.md")
|
|
55
52
|
|
|
56
|
-
# Get recent commits since last version tag
|
|
57
53
|
recent_changes = self._get_recent_changes()
|
|
58
54
|
|
|
59
55
|
if not recent_changes:
|
|
@@ -63,10 +59,8 @@ class DocumentationAgent(SubAgent):
|
|
|
63
59
|
recommendations=["No recent changes to add to changelog"],
|
|
64
60
|
)
|
|
65
61
|
|
|
66
|
-
# Generate changelog entry
|
|
67
62
|
changelog_entry = self._generate_changelog_entry(recent_changes)
|
|
68
63
|
|
|
69
|
-
# Update or create changelog
|
|
70
64
|
if changelog_path.exists():
|
|
71
65
|
content = self.context.get_file_content(changelog_path)
|
|
72
66
|
if content is None:
|
|
@@ -98,13 +92,10 @@ class DocumentationAgent(SubAgent):
|
|
|
98
92
|
)
|
|
99
93
|
|
|
100
94
|
async def _fix_documentation_consistency(self, issue: Issue) -> FixResult:
|
|
101
|
-
"""Fix consistency issues across documentation files."""
|
|
102
95
|
self.log("Checking documentation consistency")
|
|
103
96
|
|
|
104
|
-
# Find all markdown files
|
|
105
97
|
md_files = list(Path().glob("*.md")) + list(Path("docs").glob("*.md"))
|
|
106
98
|
|
|
107
|
-
# Check agent count consistency
|
|
108
99
|
agent_count_issues = self._check_agent_count_consistency(md_files)
|
|
109
100
|
|
|
110
101
|
files_modified: list[str] = []
|
|
@@ -113,7 +104,6 @@ class DocumentationAgent(SubAgent):
|
|
|
113
104
|
for file_path, current_count, expected_count in agent_count_issues:
|
|
114
105
|
content = self.context.get_file_content(file_path)
|
|
115
106
|
if content:
|
|
116
|
-
# Fix agent count references
|
|
117
107
|
updated_content = self._fix_agent_count_references(
|
|
118
108
|
content,
|
|
119
109
|
current_count,
|
|
@@ -143,10 +133,8 @@ class DocumentationAgent(SubAgent):
|
|
|
143
133
|
)
|
|
144
134
|
|
|
145
135
|
async def _update_api_documentation(self, issue: Issue) -> FixResult:
|
|
146
|
-
"""Update API documentation when public interfaces change."""
|
|
147
136
|
self.log("Updating API documentation")
|
|
148
137
|
|
|
149
|
-
# Check for API changes by analyzing recent modifications
|
|
150
138
|
api_changes = self._detect_api_changes()
|
|
151
139
|
|
|
152
140
|
if not api_changes:
|
|
@@ -158,7 +146,6 @@ class DocumentationAgent(SubAgent):
|
|
|
158
146
|
],
|
|
159
147
|
)
|
|
160
148
|
|
|
161
|
-
# Update README examples
|
|
162
149
|
readme_path = Path("README.md")
|
|
163
150
|
if readme_path.exists():
|
|
164
151
|
content = self.context.get_file_content(readme_path)
|
|
@@ -188,11 +175,8 @@ class DocumentationAgent(SubAgent):
|
|
|
188
175
|
)
|
|
189
176
|
|
|
190
177
|
async def _general_documentation_update(self, issue: Issue) -> FixResult:
|
|
191
|
-
"""Handle general documentation updates."""
|
|
192
178
|
self.log("Performing general documentation update")
|
|
193
179
|
|
|
194
|
-
# Add review comments for manual review
|
|
195
|
-
|
|
196
180
|
return FixResult(
|
|
197
181
|
success=True,
|
|
198
182
|
confidence=0.6,
|
|
@@ -204,7 +188,6 @@ class DocumentationAgent(SubAgent):
|
|
|
204
188
|
)
|
|
205
189
|
|
|
206
190
|
def _get_recent_changes(self) -> list[dict[str, str]]:
|
|
207
|
-
"""Get recent git commits since last version tag."""
|
|
208
191
|
try:
|
|
209
192
|
commit_range = self._get_commit_range()
|
|
210
193
|
if not commit_range:
|
|
@@ -217,7 +200,6 @@ class DocumentationAgent(SubAgent):
|
|
|
217
200
|
return []
|
|
218
201
|
|
|
219
202
|
def _get_commit_range(self) -> str:
|
|
220
|
-
"""Determine the commit range for changelog generation."""
|
|
221
203
|
result = subprocess.run(
|
|
222
204
|
["git", "describe", "--tags", "--abbrev=0"],
|
|
223
205
|
capture_output=True,
|
|
@@ -229,11 +211,9 @@ class DocumentationAgent(SubAgent):
|
|
|
229
211
|
last_tag = result.stdout.strip()
|
|
230
212
|
return f"{last_tag}..HEAD"
|
|
231
213
|
|
|
232
|
-
# No tags found, get last 10 commits
|
|
233
214
|
return "-10"
|
|
234
215
|
|
|
235
216
|
def _get_commit_messages(self, commit_range: str) -> str:
|
|
236
|
-
"""Get formatted commit messages for the given range."""
|
|
237
217
|
result = subprocess.run(
|
|
238
218
|
["git", "log", commit_range, "--pretty=format:%s|%h|%an"],
|
|
239
219
|
capture_output=True,
|
|
@@ -244,7 +224,6 @@ class DocumentationAgent(SubAgent):
|
|
|
244
224
|
return result.stdout.strip() if result.returncode == 0 else ""
|
|
245
225
|
|
|
246
226
|
def _parse_commit_messages(self, commit_output: str) -> list[dict[str, str]]:
|
|
247
|
-
"""Parse git log output into structured change information."""
|
|
248
227
|
changes: list[dict[str, str]] = []
|
|
249
228
|
|
|
250
229
|
for line in commit_output.split("\n"):
|
|
@@ -252,16 +231,15 @@ class DocumentationAgent(SubAgent):
|
|
|
252
231
|
parts = line.split("|")
|
|
253
232
|
if len(parts) >= 2:
|
|
254
233
|
change_info: dict[str, str] = {
|
|
255
|
-
"message": parts[0],
|
|
256
|
-
"hash": parts[1],
|
|
257
|
-
"author": parts[2] if len(parts) > 2 else "Unknown",
|
|
234
|
+
"message": parts[0].strip(),
|
|
235
|
+
"hash": parts[1].strip(),
|
|
236
|
+
"author": parts[2].strip() if len(parts) > 2 else "Unknown",
|
|
258
237
|
}
|
|
259
238
|
changes.append(change_info)
|
|
260
239
|
|
|
261
240
|
return changes
|
|
262
241
|
|
|
263
242
|
def _generate_changelog_entry(self, changes: list[dict[str, str]]) -> str:
|
|
264
|
-
"""Generate a formatted changelog entry."""
|
|
265
243
|
date_str = datetime.now().strftime("%Y-%m-%d")
|
|
266
244
|
entry_lines = [f"## [Unreleased] - {date_str}", ""]
|
|
267
245
|
|
|
@@ -274,7 +252,6 @@ class DocumentationAgent(SubAgent):
|
|
|
274
252
|
self,
|
|
275
253
|
changes: list[dict[str, str]],
|
|
276
254
|
) -> dict[str, list[str]]:
|
|
277
|
-
"""Categorize changes by type."""
|
|
278
255
|
categories: dict[str, list[str]] = {
|
|
279
256
|
"features": [],
|
|
280
257
|
"fixes": [],
|
|
@@ -290,7 +267,6 @@ class DocumentationAgent(SubAgent):
|
|
|
290
267
|
return categories
|
|
291
268
|
|
|
292
269
|
def _get_change_category(self, message: str) -> str:
|
|
293
|
-
"""Determine the category for a change message."""
|
|
294
270
|
if message.startswith(("feat:", "feature:")):
|
|
295
271
|
return "features"
|
|
296
272
|
if message.startswith("fix:"):
|
|
@@ -304,7 +280,6 @@ class DocumentationAgent(SubAgent):
|
|
|
304
280
|
entry_lines: list[str],
|
|
305
281
|
categories: dict[str, list[str]],
|
|
306
282
|
) -> None:
|
|
307
|
-
"""Add categorized changes to the entry lines."""
|
|
308
283
|
section_mappings = {
|
|
309
284
|
"features": "### Added",
|
|
310
285
|
"fixes": "### Fixed",
|
|
@@ -323,32 +298,27 @@ class DocumentationAgent(SubAgent):
|
|
|
323
298
|
section_title: str,
|
|
324
299
|
items: list[str],
|
|
325
300
|
) -> None:
|
|
326
|
-
"""Add a section with items to the entry lines."""
|
|
327
301
|
entry_lines.append(section_title)
|
|
328
302
|
for item in items:
|
|
329
303
|
entry_lines.append(f"- {item}")
|
|
330
304
|
entry_lines.append("")
|
|
331
305
|
|
|
332
306
|
def _insert_changelog_entry(self, content: str, entry: str) -> str:
|
|
333
|
-
"""Insert new changelog entry at the top."""
|
|
334
307
|
lines = content.split("\n")
|
|
335
308
|
|
|
336
|
-
# Find where to insert (after title and before first entry)
|
|
337
309
|
insert_index = 0
|
|
338
310
|
for i, line in enumerate(lines):
|
|
339
311
|
if line.startswith(("# ", "## ")):
|
|
340
|
-
if i > 0:
|
|
312
|
+
if i > 0:
|
|
341
313
|
insert_index = i
|
|
342
314
|
break
|
|
343
315
|
|
|
344
|
-
# Insert the new entry
|
|
345
316
|
new_lines = (
|
|
346
317
|
lines[:insert_index] + entry.split("\n") + [""] + lines[insert_index:]
|
|
347
318
|
)
|
|
348
319
|
return "\n".join(new_lines)
|
|
349
320
|
|
|
350
321
|
def _create_initial_changelog(self, entry: str) -> str:
|
|
351
|
-
"""Create initial CHANGELOG.md with first entry."""
|
|
352
322
|
return f"""# Changelog
|
|
353
323
|
|
|
354
324
|
All notable changes to this project will be documented in this file.
|
|
@@ -363,8 +333,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
363
333
|
self,
|
|
364
334
|
md_files: list[Path],
|
|
365
335
|
) -> list[tuple[Path, int, int]]:
|
|
366
|
-
|
|
367
|
-
expected_count = 9 # Current total with DocumentationAgent
|
|
336
|
+
expected_count = 9
|
|
368
337
|
issues: list[tuple[Path, int, int]] = []
|
|
369
338
|
patterns = self._get_agent_count_patterns()
|
|
370
339
|
|
|
@@ -376,12 +345,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
376
345
|
return issues
|
|
377
346
|
|
|
378
347
|
def _get_agent_count_patterns(self) -> list[str]:
|
|
379
|
-
"""Get regex patterns for detecting agent count references."""
|
|
380
348
|
return [
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
349
|
+
SAFE_PATTERNS["agent_count_pattern"].pattern,
|
|
350
|
+
SAFE_PATTERNS["specialized_agent_count_pattern"].pattern,
|
|
351
|
+
SAFE_PATTERNS["total_agents_config_pattern"].pattern,
|
|
352
|
+
SAFE_PATTERNS["sub_agent_count_pattern"].pattern,
|
|
385
353
|
]
|
|
386
354
|
|
|
387
355
|
def _check_file_agent_count(
|
|
@@ -390,58 +358,122 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
390
358
|
patterns: list[str],
|
|
391
359
|
expected_count: int,
|
|
392
360
|
) -> tuple[Path, int, int] | None:
|
|
393
|
-
"""Check a single file for agent count inconsistencies."""
|
|
394
361
|
with suppress(Exception):
|
|
395
362
|
content = self.context.get_file_content(file_path)
|
|
396
363
|
if not content:
|
|
397
364
|
return None
|
|
398
365
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
count = int(match)
|
|
403
|
-
if (
|
|
404
|
-
count != expected_count and count > 4
|
|
405
|
-
): # Filter out unrelated numbers
|
|
406
|
-
return (file_path, count, expected_count)
|
|
366
|
+
return self._analyze_file_content_for_agent_count(
|
|
367
|
+
file_path, content, patterns, expected_count
|
|
368
|
+
)
|
|
407
369
|
|
|
408
370
|
return None
|
|
409
371
|
|
|
372
|
+
def _analyze_file_content_for_agent_count(
|
|
373
|
+
self,
|
|
374
|
+
file_path: Path,
|
|
375
|
+
content: str,
|
|
376
|
+
patterns: list[str],
|
|
377
|
+
expected_count: int,
|
|
378
|
+
) -> tuple[Path, int, int] | None:
|
|
379
|
+
"""Analyze file content for agent count inconsistencies."""
|
|
380
|
+
pattern_map = self._get_safe_pattern_map()
|
|
381
|
+
|
|
382
|
+
for pattern in patterns:
|
|
383
|
+
result = self._check_pattern_for_count_mismatch(
|
|
384
|
+
pattern, pattern_map, content, file_path, expected_count
|
|
385
|
+
)
|
|
386
|
+
if result:
|
|
387
|
+
return result
|
|
388
|
+
|
|
389
|
+
return None
|
|
390
|
+
|
|
391
|
+
def _get_safe_pattern_map(self) -> dict[str, str]:
|
|
392
|
+
"""Get mapping of pattern strings to SAFE_PATTERNS keys."""
|
|
393
|
+
return {
|
|
394
|
+
SAFE_PATTERNS["agent_count_pattern"].pattern: "agent_count_pattern",
|
|
395
|
+
SAFE_PATTERNS[
|
|
396
|
+
"specialized_agent_count_pattern"
|
|
397
|
+
].pattern: "specialized_agent_count_pattern",
|
|
398
|
+
SAFE_PATTERNS[
|
|
399
|
+
"total_agents_config_pattern"
|
|
400
|
+
].pattern: "total_agents_config_pattern",
|
|
401
|
+
SAFE_PATTERNS["sub_agent_count_pattern"].pattern: "sub_agent_count_pattern",
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
def _check_pattern_for_count_mismatch(
|
|
405
|
+
self,
|
|
406
|
+
pattern: str,
|
|
407
|
+
pattern_map: dict[str, str],
|
|
408
|
+
content: str,
|
|
409
|
+
file_path: Path,
|
|
410
|
+
expected_count: int,
|
|
411
|
+
) -> tuple[Path, int, int] | None:
|
|
412
|
+
"""Check a specific pattern for count mismatches."""
|
|
413
|
+
if pattern not in pattern_map:
|
|
414
|
+
return None
|
|
415
|
+
|
|
416
|
+
safe_pattern = SAFE_PATTERNS[pattern_map[pattern]]
|
|
417
|
+
if not safe_pattern.test(content):
|
|
418
|
+
return None
|
|
419
|
+
|
|
420
|
+
return self._find_count_mismatch_in_matches(
|
|
421
|
+
safe_pattern, content, file_path, expected_count
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
def _find_count_mismatch_in_matches(
|
|
425
|
+
self,
|
|
426
|
+
safe_pattern: t.Any,
|
|
427
|
+
content: str,
|
|
428
|
+
file_path: Path,
|
|
429
|
+
expected_count: int,
|
|
430
|
+
) -> tuple[Path, int, int] | None:
|
|
431
|
+
"""Find count mismatches in pattern matches."""
|
|
432
|
+
matches = safe_pattern.findall(content)
|
|
433
|
+
|
|
434
|
+
for match in matches:
|
|
435
|
+
count = int(match)
|
|
436
|
+
if self._is_count_mismatch(count, expected_count):
|
|
437
|
+
return (file_path, count, expected_count)
|
|
438
|
+
|
|
439
|
+
return None
|
|
440
|
+
|
|
441
|
+
def _is_count_mismatch(self, count: int, expected_count: int) -> bool:
|
|
442
|
+
"""Check if count represents a mismatch worth reporting."""
|
|
443
|
+
return count != expected_count and count > 4
|
|
444
|
+
|
|
410
445
|
def _fix_agent_count_references(
|
|
411
446
|
self,
|
|
412
447
|
content: str,
|
|
413
448
|
current_count: int,
|
|
414
449
|
expected_count: int,
|
|
415
450
|
) -> str:
|
|
416
|
-
|
|
417
|
-
# Replace various agent count patterns
|
|
418
|
-
patterns_replacements = [
|
|
419
|
-
(rf"\b{current_count}\s+agents\b", f"{expected_count} agents"),
|
|
420
|
-
(
|
|
421
|
-
rf"\b{current_count}\s+specialized\s+agents\b",
|
|
422
|
-
f"{expected_count} specialized agents",
|
|
423
|
-
),
|
|
424
|
-
(
|
|
425
|
-
rf'total_agents["\']:\s*{current_count}',
|
|
426
|
-
f'total_agents": {expected_count}',
|
|
427
|
-
),
|
|
428
|
-
(rf"\b{current_count}\s+sub-agents\b", f"{expected_count} sub-agents"),
|
|
429
|
-
]
|
|
430
|
-
|
|
451
|
+
# Use SAFE_PATTERNS with dynamic replacement
|
|
431
452
|
updated_content = content
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
453
|
+
|
|
454
|
+
# Replace agent count references using safe patterns
|
|
455
|
+
agent_pattern = SAFE_PATTERNS["update_agent_count"]
|
|
456
|
+
specialized_pattern = SAFE_PATTERNS["update_specialized_agent_count"]
|
|
457
|
+
config_pattern = SAFE_PATTERNS["update_total_agents_config"]
|
|
458
|
+
sub_agent_pattern = SAFE_PATTERNS["update_sub_agent_count"]
|
|
459
|
+
|
|
460
|
+
# Apply patterns with dynamic replacement (NEW_COUNT -> expected_count)
|
|
461
|
+
updated_content = agent_pattern.apply(updated_content).replace(
|
|
462
|
+
"NEW_COUNT", str(expected_count)
|
|
463
|
+
)
|
|
464
|
+
updated_content = specialized_pattern.apply(updated_content).replace(
|
|
465
|
+
"NEW_COUNT", str(expected_count)
|
|
466
|
+
)
|
|
467
|
+
updated_content = config_pattern.apply(updated_content).replace(
|
|
468
|
+
"NEW_COUNT", str(expected_count)
|
|
469
|
+
)
|
|
470
|
+
updated_content = sub_agent_pattern.apply(updated_content).replace(
|
|
471
|
+
"NEW_COUNT", str(expected_count)
|
|
472
|
+
)
|
|
439
473
|
|
|
440
474
|
return updated_content
|
|
441
475
|
|
|
442
476
|
def _detect_api_changes(self) -> list[dict[str, str]]:
|
|
443
|
-
"""Detect recent API changes that might affect documentation."""
|
|
444
|
-
# This is a simplified implementation - in practice would use AST analysis
|
|
445
477
|
try:
|
|
446
478
|
result = subprocess.run(
|
|
447
479
|
["git", "diff", "--name-only", "HEAD~5..HEAD", "*.py"],
|
|
@@ -476,13 +508,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
476
508
|
content: str,
|
|
477
509
|
api_changes: list[dict[str, str]],
|
|
478
510
|
) -> str:
|
|
479
|
-
"""Update README examples based on API changes."""
|
|
480
|
-
# This is a placeholder - real implementation would parse and update code examples
|
|
481
|
-
# For now, just add a comment noting API changes
|
|
482
511
|
if api_changes and "TODO: Update examples" not in content:
|
|
483
|
-
# Add a note about updating examples
|
|
484
512
|
lines = content.split("\n")
|
|
485
|
-
|
|
513
|
+
|
|
486
514
|
for i, line in enumerate(lines):
|
|
487
515
|
if line.startswith("# ") and i < len(lines) - 1:
|
|
488
516
|
lines.insert(
|