crackerjack 0.31.10__py3-none-any.whl → 0.31.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of crackerjack might be problematic. Click here for more details.

Files changed (155) hide show
  1. crackerjack/CLAUDE.md +288 -705
  2. crackerjack/__main__.py +22 -8
  3. crackerjack/agents/__init__.py +0 -3
  4. crackerjack/agents/architect_agent.py +0 -43
  5. crackerjack/agents/base.py +1 -9
  6. crackerjack/agents/coordinator.py +2 -148
  7. crackerjack/agents/documentation_agent.py +109 -81
  8. crackerjack/agents/dry_agent.py +122 -97
  9. crackerjack/agents/formatting_agent.py +3 -16
  10. crackerjack/agents/import_optimization_agent.py +1174 -130
  11. crackerjack/agents/performance_agent.py +956 -188
  12. crackerjack/agents/performance_helpers.py +229 -0
  13. crackerjack/agents/proactive_agent.py +1 -48
  14. crackerjack/agents/refactoring_agent.py +516 -246
  15. crackerjack/agents/refactoring_helpers.py +282 -0
  16. crackerjack/agents/security_agent.py +393 -90
  17. crackerjack/agents/test_creation_agent.py +1776 -120
  18. crackerjack/agents/test_specialist_agent.py +59 -15
  19. crackerjack/agents/tracker.py +0 -102
  20. crackerjack/api.py +145 -37
  21. crackerjack/cli/handlers.py +48 -30
  22. crackerjack/cli/interactive.py +11 -11
  23. crackerjack/cli/options.py +66 -4
  24. crackerjack/code_cleaner.py +808 -148
  25. crackerjack/config/global_lock_config.py +110 -0
  26. crackerjack/config/hooks.py +43 -64
  27. crackerjack/core/async_workflow_orchestrator.py +247 -97
  28. crackerjack/core/autofix_coordinator.py +192 -109
  29. crackerjack/core/enhanced_container.py +46 -63
  30. crackerjack/core/file_lifecycle.py +549 -0
  31. crackerjack/core/performance.py +9 -8
  32. crackerjack/core/performance_monitor.py +395 -0
  33. crackerjack/core/phase_coordinator.py +281 -94
  34. crackerjack/core/proactive_workflow.py +9 -58
  35. crackerjack/core/resource_manager.py +501 -0
  36. crackerjack/core/service_watchdog.py +490 -0
  37. crackerjack/core/session_coordinator.py +4 -8
  38. crackerjack/core/timeout_manager.py +504 -0
  39. crackerjack/core/websocket_lifecycle.py +475 -0
  40. crackerjack/core/workflow_orchestrator.py +343 -209
  41. crackerjack/dynamic_config.py +50 -9
  42. crackerjack/errors.py +3 -4
  43. crackerjack/executors/async_hook_executor.py +63 -13
  44. crackerjack/executors/cached_hook_executor.py +14 -14
  45. crackerjack/executors/hook_executor.py +100 -37
  46. crackerjack/executors/hook_lock_manager.py +856 -0
  47. crackerjack/executors/individual_hook_executor.py +120 -86
  48. crackerjack/intelligence/__init__.py +0 -7
  49. crackerjack/intelligence/adaptive_learning.py +13 -86
  50. crackerjack/intelligence/agent_orchestrator.py +15 -78
  51. crackerjack/intelligence/agent_registry.py +12 -59
  52. crackerjack/intelligence/agent_selector.py +31 -92
  53. crackerjack/intelligence/integration.py +1 -41
  54. crackerjack/interactive.py +9 -9
  55. crackerjack/managers/async_hook_manager.py +25 -8
  56. crackerjack/managers/hook_manager.py +9 -9
  57. crackerjack/managers/publish_manager.py +57 -59
  58. crackerjack/managers/test_command_builder.py +6 -36
  59. crackerjack/managers/test_executor.py +9 -61
  60. crackerjack/managers/test_manager.py +17 -63
  61. crackerjack/managers/test_manager_backup.py +77 -127
  62. crackerjack/managers/test_progress.py +4 -23
  63. crackerjack/mcp/cache.py +5 -12
  64. crackerjack/mcp/client_runner.py +10 -10
  65. crackerjack/mcp/context.py +64 -6
  66. crackerjack/mcp/dashboard.py +14 -11
  67. crackerjack/mcp/enhanced_progress_monitor.py +55 -55
  68. crackerjack/mcp/file_monitor.py +72 -42
  69. crackerjack/mcp/progress_components.py +103 -84
  70. crackerjack/mcp/progress_monitor.py +122 -49
  71. crackerjack/mcp/rate_limiter.py +12 -12
  72. crackerjack/mcp/server_core.py +16 -22
  73. crackerjack/mcp/service_watchdog.py +26 -26
  74. crackerjack/mcp/state.py +15 -0
  75. crackerjack/mcp/tools/core_tools.py +95 -39
  76. crackerjack/mcp/tools/error_analyzer.py +6 -32
  77. crackerjack/mcp/tools/execution_tools.py +1 -56
  78. crackerjack/mcp/tools/execution_tools_backup.py +35 -131
  79. crackerjack/mcp/tools/intelligence_tool_registry.py +0 -36
  80. crackerjack/mcp/tools/intelligence_tools.py +2 -55
  81. crackerjack/mcp/tools/monitoring_tools.py +308 -145
  82. crackerjack/mcp/tools/proactive_tools.py +12 -42
  83. crackerjack/mcp/tools/progress_tools.py +23 -15
  84. crackerjack/mcp/tools/utility_tools.py +3 -40
  85. crackerjack/mcp/tools/workflow_executor.py +40 -60
  86. crackerjack/mcp/websocket/app.py +0 -3
  87. crackerjack/mcp/websocket/endpoints.py +206 -268
  88. crackerjack/mcp/websocket/jobs.py +213 -66
  89. crackerjack/mcp/websocket/server.py +84 -6
  90. crackerjack/mcp/websocket/websocket_handler.py +137 -29
  91. crackerjack/models/config_adapter.py +3 -16
  92. crackerjack/models/protocols.py +162 -3
  93. crackerjack/models/resource_protocols.py +454 -0
  94. crackerjack/models/task.py +3 -3
  95. crackerjack/monitoring/__init__.py +0 -0
  96. crackerjack/monitoring/ai_agent_watchdog.py +25 -71
  97. crackerjack/monitoring/regression_prevention.py +28 -87
  98. crackerjack/orchestration/advanced_orchestrator.py +44 -78
  99. crackerjack/orchestration/coverage_improvement.py +10 -60
  100. crackerjack/orchestration/execution_strategies.py +16 -16
  101. crackerjack/orchestration/test_progress_streamer.py +61 -53
  102. crackerjack/plugins/base.py +1 -1
  103. crackerjack/plugins/managers.py +22 -20
  104. crackerjack/py313.py +65 -21
  105. crackerjack/services/backup_service.py +467 -0
  106. crackerjack/services/bounded_status_operations.py +627 -0
  107. crackerjack/services/cache.py +7 -9
  108. crackerjack/services/config.py +35 -52
  109. crackerjack/services/config_integrity.py +5 -16
  110. crackerjack/services/config_merge.py +542 -0
  111. crackerjack/services/contextual_ai_assistant.py +17 -19
  112. crackerjack/services/coverage_ratchet.py +44 -73
  113. crackerjack/services/debug.py +25 -39
  114. crackerjack/services/dependency_monitor.py +52 -50
  115. crackerjack/services/enhanced_filesystem.py +14 -11
  116. crackerjack/services/file_hasher.py +1 -1
  117. crackerjack/services/filesystem.py +1 -12
  118. crackerjack/services/git.py +71 -47
  119. crackerjack/services/health_metrics.py +31 -27
  120. crackerjack/services/initialization.py +276 -428
  121. crackerjack/services/input_validator.py +760 -0
  122. crackerjack/services/log_manager.py +16 -16
  123. crackerjack/services/logging.py +7 -6
  124. crackerjack/services/metrics.py +43 -43
  125. crackerjack/services/pattern_cache.py +2 -31
  126. crackerjack/services/pattern_detector.py +26 -63
  127. crackerjack/services/performance_benchmarks.py +20 -45
  128. crackerjack/services/regex_patterns.py +2887 -0
  129. crackerjack/services/regex_utils.py +537 -0
  130. crackerjack/services/secure_path_utils.py +683 -0
  131. crackerjack/services/secure_status_formatter.py +534 -0
  132. crackerjack/services/secure_subprocess.py +605 -0
  133. crackerjack/services/security.py +47 -10
  134. crackerjack/services/security_logger.py +492 -0
  135. crackerjack/services/server_manager.py +109 -50
  136. crackerjack/services/smart_scheduling.py +8 -25
  137. crackerjack/services/status_authentication.py +603 -0
  138. crackerjack/services/status_security_manager.py +442 -0
  139. crackerjack/services/thread_safe_status_collector.py +546 -0
  140. crackerjack/services/tool_version_service.py +1 -23
  141. crackerjack/services/unified_config.py +36 -58
  142. crackerjack/services/validation_rate_limiter.py +269 -0
  143. crackerjack/services/version_checker.py +9 -40
  144. crackerjack/services/websocket_resource_limiter.py +572 -0
  145. crackerjack/slash_commands/__init__.py +52 -2
  146. crackerjack/tools/__init__.py +0 -0
  147. crackerjack/tools/validate_input_validator_patterns.py +262 -0
  148. crackerjack/tools/validate_regex_patterns.py +198 -0
  149. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/METADATA +197 -12
  150. crackerjack-0.31.13.dist-info/RECORD +178 -0
  151. crackerjack/cli/facade.py +0 -104
  152. crackerjack-0.31.10.dist-info/RECORD +0 -149
  153. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/WHEEL +0 -0
  154. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.dist-info}/entry_points.txt +0 -0
  155. {crackerjack-0.31.10.dist-info → crackerjack-0.31.13.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: # Skip the main title
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
- """Check for inconsistent agent count references across documentation."""
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
- r"(\d+)\s+agents",
382
- r"(\d+)\s+specialized\s+agents",
383
- r'total_agents["\']:\s*(\d+)',
384
- r"(\d+)\s+sub-agents",
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
- for pattern in patterns:
400
- matches = re.findall(pattern, content, re.IGNORECASE)
401
- for match in matches:
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
- """Fix agent count references in documentation."""
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
- for pattern, replacement in patterns_replacements:
433
- updated_content = re.sub(
434
- pattern,
435
- replacement,
436
- updated_content,
437
- flags=re.IGNORECASE,
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
- # Insert near the top after the title
513
+
486
514
  for i, line in enumerate(lines):
487
515
  if line.startswith("# ") and i < len(lines) - 1:
488
516
  lines.insert(