crackerjack 0.32.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.
- crackerjack/__main__.py +1350 -34
- crackerjack/adapters/__init__.py +17 -0
- crackerjack/adapters/lsp_client.py +358 -0
- crackerjack/adapters/rust_tool_adapter.py +194 -0
- crackerjack/adapters/rust_tool_manager.py +193 -0
- crackerjack/adapters/skylos_adapter.py +231 -0
- crackerjack/adapters/zuban_adapter.py +560 -0
- crackerjack/agents/base.py +7 -3
- crackerjack/agents/coordinator.py +271 -33
- crackerjack/agents/documentation_agent.py +9 -15
- crackerjack/agents/dry_agent.py +3 -15
- crackerjack/agents/formatting_agent.py +1 -1
- crackerjack/agents/import_optimization_agent.py +36 -180
- crackerjack/agents/performance_agent.py +17 -98
- crackerjack/agents/performance_helpers.py +7 -31
- crackerjack/agents/proactive_agent.py +1 -3
- crackerjack/agents/refactoring_agent.py +16 -85
- crackerjack/agents/refactoring_helpers.py +7 -42
- crackerjack/agents/security_agent.py +9 -48
- crackerjack/agents/test_creation_agent.py +356 -513
- crackerjack/agents/test_specialist_agent.py +0 -4
- crackerjack/api.py +6 -25
- crackerjack/cli/cache_handlers.py +204 -0
- crackerjack/cli/cache_handlers_enhanced.py +683 -0
- crackerjack/cli/facade.py +100 -0
- crackerjack/cli/handlers.py +224 -9
- crackerjack/cli/interactive.py +6 -4
- crackerjack/cli/options.py +642 -55
- crackerjack/cli/utils.py +2 -1
- crackerjack/code_cleaner.py +58 -117
- crackerjack/config/global_lock_config.py +8 -48
- crackerjack/config/hooks.py +53 -62
- crackerjack/core/async_workflow_orchestrator.py +24 -34
- crackerjack/core/autofix_coordinator.py +3 -17
- crackerjack/core/enhanced_container.py +64 -6
- crackerjack/core/file_lifecycle.py +12 -89
- crackerjack/core/performance.py +2 -2
- crackerjack/core/performance_monitor.py +15 -55
- crackerjack/core/phase_coordinator.py +257 -218
- crackerjack/core/resource_manager.py +14 -90
- crackerjack/core/service_watchdog.py +62 -95
- crackerjack/core/session_coordinator.py +149 -0
- crackerjack/core/timeout_manager.py +14 -72
- crackerjack/core/websocket_lifecycle.py +13 -78
- crackerjack/core/workflow_orchestrator.py +558 -240
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
- crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
- crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
- crackerjack/docs/generated/api/SERVICES.md +1252 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +765 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +977 -0
- crackerjack/dynamic_config.py +55 -50
- crackerjack/executors/async_hook_executor.py +10 -15
- crackerjack/executors/cached_hook_executor.py +117 -43
- crackerjack/executors/hook_executor.py +8 -34
- crackerjack/executors/hook_lock_manager.py +26 -183
- crackerjack/executors/individual_hook_executor.py +13 -11
- crackerjack/executors/lsp_aware_hook_executor.py +270 -0
- crackerjack/executors/tool_proxy.py +417 -0
- crackerjack/hooks/lsp_hook.py +79 -0
- crackerjack/intelligence/adaptive_learning.py +25 -10
- crackerjack/intelligence/agent_orchestrator.py +2 -5
- crackerjack/intelligence/agent_registry.py +34 -24
- crackerjack/intelligence/agent_selector.py +5 -7
- crackerjack/interactive.py +17 -6
- crackerjack/managers/async_hook_manager.py +0 -1
- crackerjack/managers/hook_manager.py +79 -1
- crackerjack/managers/publish_manager.py +66 -13
- crackerjack/managers/test_command_builder.py +5 -17
- crackerjack/managers/test_executor.py +1 -3
- crackerjack/managers/test_manager.py +109 -7
- crackerjack/managers/test_manager_backup.py +10 -9
- crackerjack/mcp/cache.py +2 -2
- crackerjack/mcp/client_runner.py +1 -1
- crackerjack/mcp/context.py +191 -68
- crackerjack/mcp/dashboard.py +7 -5
- crackerjack/mcp/enhanced_progress_monitor.py +31 -28
- crackerjack/mcp/file_monitor.py +30 -23
- crackerjack/mcp/progress_components.py +31 -21
- crackerjack/mcp/progress_monitor.py +50 -53
- crackerjack/mcp/rate_limiter.py +6 -6
- crackerjack/mcp/server_core.py +161 -32
- crackerjack/mcp/service_watchdog.py +2 -1
- crackerjack/mcp/state.py +4 -7
- crackerjack/mcp/task_manager.py +11 -9
- crackerjack/mcp/tools/core_tools.py +174 -33
- crackerjack/mcp/tools/error_analyzer.py +3 -2
- crackerjack/mcp/tools/execution_tools.py +15 -12
- crackerjack/mcp/tools/execution_tools_backup.py +42 -30
- crackerjack/mcp/tools/intelligence_tool_registry.py +7 -5
- crackerjack/mcp/tools/intelligence_tools.py +5 -2
- crackerjack/mcp/tools/monitoring_tools.py +33 -70
- crackerjack/mcp/tools/proactive_tools.py +24 -11
- crackerjack/mcp/tools/progress_tools.py +5 -8
- crackerjack/mcp/tools/utility_tools.py +20 -14
- crackerjack/mcp/tools/workflow_executor.py +62 -40
- crackerjack/mcp/websocket/app.py +8 -0
- crackerjack/mcp/websocket/endpoints.py +352 -357
- crackerjack/mcp/websocket/jobs.py +40 -57
- crackerjack/mcp/websocket/monitoring_endpoints.py +2935 -0
- crackerjack/mcp/websocket/server.py +7 -25
- crackerjack/mcp/websocket/websocket_handler.py +6 -17
- crackerjack/mixins/__init__.py +3 -0
- crackerjack/mixins/error_handling.py +145 -0
- crackerjack/models/config.py +21 -1
- crackerjack/models/config_adapter.py +49 -1
- crackerjack/models/protocols.py +176 -107
- crackerjack/models/resource_protocols.py +55 -210
- crackerjack/models/task.py +3 -0
- crackerjack/monitoring/ai_agent_watchdog.py +13 -13
- crackerjack/monitoring/metrics_collector.py +426 -0
- crackerjack/monitoring/regression_prevention.py +8 -8
- crackerjack/monitoring/websocket_server.py +643 -0
- crackerjack/orchestration/advanced_orchestrator.py +11 -6
- crackerjack/orchestration/coverage_improvement.py +3 -3
- crackerjack/orchestration/execution_strategies.py +26 -6
- crackerjack/orchestration/test_progress_streamer.py +8 -5
- crackerjack/plugins/base.py +2 -2
- crackerjack/plugins/hooks.py +7 -0
- crackerjack/plugins/managers.py +11 -8
- crackerjack/security/__init__.py +0 -1
- crackerjack/security/audit.py +90 -105
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +615 -0
- crackerjack/services/backup_service.py +2 -2
- crackerjack/services/bounded_status_operations.py +15 -152
- crackerjack/services/cache.py +127 -1
- crackerjack/services/changelog_automation.py +395 -0
- crackerjack/services/config.py +18 -11
- crackerjack/services/config_merge.py +30 -85
- crackerjack/services/config_template.py +506 -0
- crackerjack/services/contextual_ai_assistant.py +48 -22
- crackerjack/services/coverage_badge_service.py +171 -0
- crackerjack/services/coverage_ratchet.py +41 -17
- crackerjack/services/debug.py +3 -3
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +14 -11
- crackerjack/services/documentation_generator.py +491 -0
- crackerjack/services/documentation_service.py +675 -0
- crackerjack/services/enhanced_filesystem.py +6 -5
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_hasher.py +1 -1
- crackerjack/services/git.py +41 -45
- crackerjack/services/health_metrics.py +10 -8
- crackerjack/services/heatmap_generator.py +735 -0
- crackerjack/services/initialization.py +30 -33
- crackerjack/services/input_validator.py +5 -97
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +15 -12
- crackerjack/services/logging.py +4 -3
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +409 -0
- crackerjack/services/metrics.py +42 -33
- crackerjack/services/parallel_executor.py +416 -0
- crackerjack/services/pattern_cache.py +1 -1
- crackerjack/services/pattern_detector.py +6 -6
- crackerjack/services/performance_benchmarks.py +250 -576
- crackerjack/services/performance_cache.py +382 -0
- crackerjack/services/performance_monitor.py +565 -0
- crackerjack/services/predictive_analytics.py +510 -0
- crackerjack/services/quality_baseline.py +234 -0
- crackerjack/services/quality_baseline_enhanced.py +646 -0
- crackerjack/services/quality_intelligence.py +785 -0
- crackerjack/services/regex_patterns.py +605 -524
- crackerjack/services/regex_utils.py +43 -123
- crackerjack/services/secure_path_utils.py +5 -164
- crackerjack/services/secure_status_formatter.py +30 -141
- crackerjack/services/secure_subprocess.py +11 -92
- crackerjack/services/security.py +61 -30
- crackerjack/services/security_logger.py +18 -22
- crackerjack/services/server_manager.py +124 -16
- crackerjack/services/status_authentication.py +16 -159
- crackerjack/services/status_security_manager.py +4 -131
- crackerjack/services/terminal_utils.py +0 -0
- crackerjack/services/thread_safe_status_collector.py +19 -125
- crackerjack/services/unified_config.py +21 -13
- crackerjack/services/validation_rate_limiter.py +5 -54
- crackerjack/services/version_analyzer.py +459 -0
- crackerjack/services/version_checker.py +1 -1
- crackerjack/services/websocket_resource_limiter.py +10 -144
- crackerjack/services/zuban_lsp_service.py +390 -0
- crackerjack/slash_commands/__init__.py +2 -7
- crackerjack/slash_commands/run.md +2 -2
- crackerjack/tools/validate_input_validator_patterns.py +14 -40
- crackerjack/tools/validate_regex_patterns.py +19 -48
- {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/METADATA +197 -26
- crackerjack-0.33.1.dist-info/RECORD +229 -0
- crackerjack/CLAUDE.md +0 -207
- crackerjack/RULES.md +0 -380
- crackerjack/py313.py +0 -234
- crackerjack-0.32.0.dist-info/RECORD +0 -180
- {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/WHEEL +0 -0
- {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.32.0.dist-info → crackerjack-0.33.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
from contextlib import suppress
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
|
|
6
|
+
from .regex_patterns import SAFE_PATTERNS
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CoverageBadgeService:
|
|
10
|
+
"""Service for managing coverage badges in README.md files."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, console: Console, project_root: Path) -> None:
|
|
13
|
+
self.console = console
|
|
14
|
+
self.project_root = project_root
|
|
15
|
+
self.readme_path = project_root / "README.md"
|
|
16
|
+
|
|
17
|
+
def update_readme_coverage_badge(self, coverage_percent: float) -> bool:
|
|
18
|
+
"""Update or insert coverage badge in README.md with current coverage percentage."""
|
|
19
|
+
if not self.readme_path.exists():
|
|
20
|
+
self.console.print(
|
|
21
|
+
"[yellow]⚠️[/yellow] README.md not found, skipping badge update"
|
|
22
|
+
)
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
readme_content = self.readme_path.read_text(encoding="utf-8")
|
|
27
|
+
badge_url = self._generate_badge_url(coverage_percent)
|
|
28
|
+
|
|
29
|
+
if self._has_coverage_badge(readme_content):
|
|
30
|
+
updated_content = self._update_existing_badge(readme_content, badge_url)
|
|
31
|
+
action = "updated"
|
|
32
|
+
else:
|
|
33
|
+
updated_content = self._insert_new_badge(readme_content, badge_url)
|
|
34
|
+
action = "added"
|
|
35
|
+
|
|
36
|
+
if updated_content != readme_content:
|
|
37
|
+
self.readme_path.write_text(updated_content, encoding="utf-8")
|
|
38
|
+
self.console.print(
|
|
39
|
+
f"[green]📊[/green] Coverage badge {action}: {coverage_percent:.1f}%"
|
|
40
|
+
)
|
|
41
|
+
return True
|
|
42
|
+
else:
|
|
43
|
+
return False
|
|
44
|
+
|
|
45
|
+
except Exception as e:
|
|
46
|
+
self.console.print(f"[red]❌[/red] Failed to update coverage badge: {e}")
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
def _generate_badge_url(self, coverage_percent: float) -> str:
|
|
50
|
+
"""Generate shields.io badge URL with appropriate color coding."""
|
|
51
|
+
color = self._get_badge_color(coverage_percent)
|
|
52
|
+
# URL encode the % symbol as %25
|
|
53
|
+
encoded_percent = f"{coverage_percent:.1f}%25"
|
|
54
|
+
return f"https://img.shields.io/badge/coverage-{encoded_percent}-{color}"
|
|
55
|
+
|
|
56
|
+
def _get_badge_color(self, coverage_percent: float) -> str:
|
|
57
|
+
"""Determine badge color based on coverage percentage."""
|
|
58
|
+
if coverage_percent < 50:
|
|
59
|
+
return "red"
|
|
60
|
+
elif coverage_percent < 80:
|
|
61
|
+
return "yellow"
|
|
62
|
+
return "brightgreen"
|
|
63
|
+
|
|
64
|
+
def _has_coverage_badge(self, content: str) -> bool:
|
|
65
|
+
"""Check if README already contains a coverage badge."""
|
|
66
|
+
# Use safe pattern for badge detection
|
|
67
|
+
return SAFE_PATTERNS["detect_coverage_badge"].search(content) is not None
|
|
68
|
+
|
|
69
|
+
def _update_existing_badge(self, content: str, new_badge_url: str) -> str:
|
|
70
|
+
"""Replace existing coverage badge with new one."""
|
|
71
|
+
# Try different safe patterns for badge replacement
|
|
72
|
+
patterns_to_try = [
|
|
73
|
+
"update_coverage_badge_url",
|
|
74
|
+
"update_coverage_badge_any",
|
|
75
|
+
"update_shields_coverage_url",
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
for pattern_name in patterns_to_try:
|
|
79
|
+
pattern_obj = SAFE_PATTERNS[pattern_name]
|
|
80
|
+
# Use the pattern and manually replace NEW_BADGE_URL with actual URL
|
|
81
|
+
temp_content = pattern_obj.apply(content)
|
|
82
|
+
if temp_content != content:
|
|
83
|
+
# Replace placeholder with actual URL
|
|
84
|
+
new_content = temp_content.replace("NEW_BADGE_URL", new_badge_url)
|
|
85
|
+
return new_content
|
|
86
|
+
|
|
87
|
+
return content
|
|
88
|
+
|
|
89
|
+
def _insert_new_badge(self, content: str, badge_url: str) -> str:
|
|
90
|
+
"""Insert new coverage badge in the appropriate location."""
|
|
91
|
+
lines = content.split("\n")
|
|
92
|
+
|
|
93
|
+
# Find the badge section (after title, before first heading)
|
|
94
|
+
insert_index = self._find_badge_insertion_point(lines)
|
|
95
|
+
|
|
96
|
+
if insert_index is not None:
|
|
97
|
+
coverage_badge = f""
|
|
98
|
+
lines.insert(insert_index, coverage_badge)
|
|
99
|
+
return "\n".join(lines)
|
|
100
|
+
# Fallback: add after title
|
|
101
|
+
return self._insert_after_title(content, badge_url)
|
|
102
|
+
|
|
103
|
+
def _find_badge_insertion_point(self, lines: list[str]) -> int | None:
|
|
104
|
+
"""Find the best location to insert the coverage badge."""
|
|
105
|
+
# Look for existing badge lines
|
|
106
|
+
badge_lines = [
|
|
107
|
+
i for i, line in enumerate(lines) if line.strip().startswith(("[![", "!["))
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
if badge_lines:
|
|
111
|
+
# Insert after the last existing badge
|
|
112
|
+
return badge_lines[-1] + 1
|
|
113
|
+
|
|
114
|
+
# Look for first non-empty line after title
|
|
115
|
+
title_found = False
|
|
116
|
+
for i, line in enumerate(lines):
|
|
117
|
+
if line.startswith("#") and not title_found:
|
|
118
|
+
title_found = True
|
|
119
|
+
continue
|
|
120
|
+
elif title_found and line.strip() == "":
|
|
121
|
+
continue
|
|
122
|
+
elif title_found and line.strip():
|
|
123
|
+
return i
|
|
124
|
+
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
def _insert_after_title(self, content: str, badge_url: str) -> str:
|
|
128
|
+
"""Fallback method to insert badge after the title."""
|
|
129
|
+
lines = content.split("\n")
|
|
130
|
+
|
|
131
|
+
# Find title line
|
|
132
|
+
for i, line in enumerate(lines):
|
|
133
|
+
if line.startswith("#"):
|
|
134
|
+
# Insert after title with blank line
|
|
135
|
+
coverage_badge = f""
|
|
136
|
+
if i + 1 < len(lines) and lines[i + 1].strip() == "":
|
|
137
|
+
lines.insert(i + 2, coverage_badge)
|
|
138
|
+
else:
|
|
139
|
+
lines.insert(i + 1, "")
|
|
140
|
+
lines.insert(i + 2, coverage_badge)
|
|
141
|
+
break
|
|
142
|
+
|
|
143
|
+
return "\n".join(lines)
|
|
144
|
+
|
|
145
|
+
def should_update_badge(self, coverage_percent: float) -> bool:
|
|
146
|
+
"""Check if badge should be updated based on coverage change."""
|
|
147
|
+
if not self.readme_path.exists():
|
|
148
|
+
return False
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
content = self.readme_path.read_text(encoding="utf-8")
|
|
152
|
+
current_coverage = self._extract_current_coverage(content)
|
|
153
|
+
|
|
154
|
+
if current_coverage is None:
|
|
155
|
+
return True # No badge exists, should add one
|
|
156
|
+
|
|
157
|
+
# Only update if coverage changed by at least 0.1%
|
|
158
|
+
return abs(coverage_percent - current_coverage) >= 0.1
|
|
159
|
+
|
|
160
|
+
except Exception:
|
|
161
|
+
return True # On error, attempt update
|
|
162
|
+
|
|
163
|
+
def _extract_current_coverage(self, content: str) -> float | None:
|
|
164
|
+
"""Extract current coverage percentage from existing badge."""
|
|
165
|
+
match = SAFE_PATTERNS["extract_coverage_percentage"].search(content)
|
|
166
|
+
|
|
167
|
+
if match:
|
|
168
|
+
with suppress(ValueError):
|
|
169
|
+
return float(match.group(1))
|
|
170
|
+
|
|
171
|
+
return None
|
|
@@ -9,7 +9,7 @@ from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn
|
|
|
9
9
|
|
|
10
10
|
class CoverageRatchetService:
|
|
11
11
|
MILESTONES = [15, 20, 25, 30, 40, 50, 60, 70, 80, 90, 95, 100]
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
TOLERANCE_MARGIN = 2.0
|
|
14
14
|
|
|
15
15
|
def __init__(self, pkg_path: Path, console: Console) -> None:
|
|
@@ -22,7 +22,7 @@ class CoverageRatchetService:
|
|
|
22
22
|
if self.ratchet_file.exists():
|
|
23
23
|
return
|
|
24
24
|
|
|
25
|
-
ratchet_data = {
|
|
25
|
+
ratchet_data: dict[str, t.Any] = {
|
|
26
26
|
"baseline": initial_coverage,
|
|
27
27
|
"current_minimum": initial_coverage,
|
|
28
28
|
"target": 100.0,
|
|
@@ -47,22 +47,39 @@ class CoverageRatchetService:
|
|
|
47
47
|
def get_ratchet_data(self) -> dict[str, t.Any]:
|
|
48
48
|
if not self.ratchet_file.exists():
|
|
49
49
|
return {}
|
|
50
|
-
return json.loads(self.ratchet_file.read_text())
|
|
50
|
+
return t.cast(dict[str, t.Any], json.loads(self.ratchet_file.read_text()))
|
|
51
|
+
|
|
52
|
+
def get_status_report(self) -> dict[str, t.Any]:
|
|
53
|
+
"""Get status report for coverage ratchet service."""
|
|
54
|
+
return self.get_ratchet_data()
|
|
51
55
|
|
|
52
56
|
def get_baseline(self) -> float:
|
|
53
|
-
|
|
57
|
+
data = self.get_ratchet_data()
|
|
58
|
+
baseline = data.get("baseline")
|
|
59
|
+
return float(baseline) if baseline is not None else 0.0
|
|
54
60
|
|
|
55
|
-
def
|
|
56
|
-
|
|
61
|
+
def get_baseline_coverage(self) -> float:
|
|
62
|
+
return self.get_baseline()
|
|
63
|
+
|
|
64
|
+
def update_baseline_coverage(self, new_coverage: float) -> bool:
|
|
65
|
+
result: bool = self.update_coverage(new_coverage).get("success", False)
|
|
66
|
+
return result
|
|
57
67
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
- Coverage above baseline: PASS (improved, updates baseline)
|
|
68
|
+
def is_coverage_regression(self, current_coverage: float) -> bool:
|
|
69
|
+
baseline = self.get_baseline()
|
|
70
|
+
return current_coverage < (baseline - self.TOLERANCE_MARGIN)
|
|
62
71
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
""
|
|
72
|
+
def calculate_coverage_gap(self) -> float:
|
|
73
|
+
data = self.get_ratchet_data()
|
|
74
|
+
baseline = data.get("baseline")
|
|
75
|
+
baseline = float(baseline) if baseline is not None else 0.0
|
|
76
|
+
next_milestone = data.get("next_milestone")
|
|
77
|
+
next_milestone = float(next_milestone) if next_milestone is not None else None
|
|
78
|
+
if next_milestone:
|
|
79
|
+
return next_milestone - baseline
|
|
80
|
+
return 100.0 - baseline
|
|
81
|
+
|
|
82
|
+
def update_coverage(self, new_coverage: float) -> dict[str, t.Any]:
|
|
66
83
|
if not self.ratchet_file.exists():
|
|
67
84
|
self.initialize_baseline(new_coverage)
|
|
68
85
|
return {
|
|
@@ -77,7 +94,6 @@ class CoverageRatchetService:
|
|
|
77
94
|
data = self.get_ratchet_data()
|
|
78
95
|
current_baseline = data["baseline"]
|
|
79
96
|
|
|
80
|
-
# Check if coverage is below the tolerance margin (baseline - 2%)
|
|
81
97
|
tolerance_threshold = current_baseline - self.TOLERANCE_MARGIN
|
|
82
98
|
if new_coverage < tolerance_threshold:
|
|
83
99
|
return {
|
|
@@ -104,11 +120,12 @@ class CoverageRatchetService:
|
|
|
104
120
|
"next_milestone": self._get_next_milestone(new_coverage),
|
|
105
121
|
"points_to_next": (next_milestone - new_coverage)
|
|
106
122
|
if (next_milestone := self._get_next_milestone(new_coverage))
|
|
123
|
+
is not None
|
|
107
124
|
else 0,
|
|
108
125
|
"allowed": True,
|
|
109
126
|
"baseline_updated": True,
|
|
110
127
|
}
|
|
111
|
-
|
|
128
|
+
|
|
112
129
|
return {
|
|
113
130
|
"status": "maintained",
|
|
114
131
|
"message": f"Coverage maintained at {new_coverage: .2f}% (within {self.TOLERANCE_MARGIN}% tolerance margin)",
|
|
@@ -119,7 +136,7 @@ class CoverageRatchetService:
|
|
|
119
136
|
def _check_milestones(
|
|
120
137
|
self, old_coverage: float, new_coverage: float, data: dict[str, t.Any]
|
|
121
138
|
) -> list[float]:
|
|
122
|
-
achieved_milestones = set(data.get("milestones_achieved", []))
|
|
139
|
+
achieved_milestones = set[t.Any](data.get("milestones_achieved", []))
|
|
123
140
|
return [
|
|
124
141
|
milestone
|
|
125
142
|
for milestone in self.MILESTONES
|
|
@@ -211,7 +228,14 @@ class CoverageRatchetService:
|
|
|
211
228
|
|
|
212
229
|
return result
|
|
213
230
|
|
|
214
|
-
def
|
|
231
|
+
def get_coverage_improvement_needed(self) -> float:
|
|
232
|
+
"""Get percentage improvement needed to reach next milestone."""
|
|
233
|
+
current = self.get_baseline_coverage()
|
|
234
|
+
for milestone in self.MILESTONES:
|
|
235
|
+
if current < milestone:
|
|
236
|
+
needed = milestone - current
|
|
237
|
+
return max(0.0, needed)
|
|
238
|
+
return 0.0
|
|
215
239
|
data = self.get_ratchet_data()
|
|
216
240
|
if not data:
|
|
217
241
|
return {"status": "not_initialized"}
|
crackerjack/services/debug.py
CHANGED
|
@@ -24,13 +24,13 @@ class AIAgentDebugger:
|
|
|
24
24
|
self.logger = get_logger("crackerjack.ai_agent.debug")
|
|
25
25
|
self.session_id = f"debug_{int(time.time())}"
|
|
26
26
|
|
|
27
|
+
self.debug_log_path: Path | None = None
|
|
28
|
+
|
|
27
29
|
if self.enabled:
|
|
28
30
|
log_manager = get_log_manager()
|
|
29
31
|
self.debug_log_path = log_manager.create_debug_log_file(
|
|
30
32
|
f"ai-agent -{self.session_id}",
|
|
31
33
|
)
|
|
32
|
-
else:
|
|
33
|
-
self.debug_log_path = None
|
|
34
34
|
|
|
35
35
|
self.mcp_operations: list[dict[str, Any]] = []
|
|
36
36
|
self.agent_activities: list[dict[str, Any]] = []
|
|
@@ -400,7 +400,7 @@ class AIAgentDebugger:
|
|
|
400
400
|
self.console.print(table)
|
|
401
401
|
|
|
402
402
|
def _print_agent_activity_breakdown(self, border_style: str = "red") -> None:
|
|
403
|
-
agent_stats = {}
|
|
403
|
+
agent_stats: dict[str, dict[str, t.Any]] = {}
|
|
404
404
|
for activity in self.agent_activities:
|
|
405
405
|
agent = activity["agent"]
|
|
406
406
|
if agent not in agent_stats:
|