claude-mpm 4.13.2__py3-none-any.whl → 4.18.2__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +48 -17
- claude_mpm/agents/OUTPUT_STYLE.md +329 -11
- claude_mpm/agents/PM_INSTRUCTIONS.md +227 -8
- claude_mpm/agents/agent_loader.py +17 -5
- claude_mpm/agents/frontmatter_validator.py +284 -253
- claude_mpm/agents/templates/agentic-coder-optimizer.json +9 -2
- claude_mpm/agents/templates/api_qa.json +7 -1
- claude_mpm/agents/templates/clerk-ops.json +8 -1
- claude_mpm/agents/templates/code_analyzer.json +4 -1
- claude_mpm/agents/templates/dart_engineer.json +11 -1
- claude_mpm/agents/templates/data_engineer.json +11 -1
- claude_mpm/agents/templates/documentation.json +6 -1
- claude_mpm/agents/templates/engineer.json +18 -1
- claude_mpm/agents/templates/gcp_ops_agent.json +8 -1
- claude_mpm/agents/templates/golang_engineer.json +11 -1
- claude_mpm/agents/templates/java_engineer.json +12 -2
- claude_mpm/agents/templates/local_ops_agent.json +1217 -6
- claude_mpm/agents/templates/nextjs_engineer.json +11 -1
- claude_mpm/agents/templates/ops.json +8 -1
- claude_mpm/agents/templates/php-engineer.json +11 -1
- claude_mpm/agents/templates/project_organizer.json +10 -3
- claude_mpm/agents/templates/prompt-engineer.json +5 -1
- claude_mpm/agents/templates/python_engineer.json +11 -1
- claude_mpm/agents/templates/qa.json +7 -1
- claude_mpm/agents/templates/react_engineer.json +11 -1
- claude_mpm/agents/templates/refactoring_engineer.json +8 -1
- claude_mpm/agents/templates/research.json +4 -1
- claude_mpm/agents/templates/ruby-engineer.json +11 -1
- claude_mpm/agents/templates/rust_engineer.json +11 -1
- claude_mpm/agents/templates/security.json +6 -1
- claude_mpm/agents/templates/svelte-engineer.json +225 -0
- claude_mpm/agents/templates/ticketing.json +6 -1
- claude_mpm/agents/templates/typescript_engineer.json +11 -1
- claude_mpm/agents/templates/vercel_ops_agent.json +8 -1
- claude_mpm/agents/templates/version_control.json +8 -1
- claude_mpm/agents/templates/web_qa.json +7 -1
- claude_mpm/agents/templates/web_ui.json +11 -1
- claude_mpm/cli/__init__.py +34 -706
- claude_mpm/cli/commands/agent_manager.py +25 -12
- claude_mpm/cli/commands/agent_state_manager.py +186 -0
- claude_mpm/cli/commands/agents.py +204 -148
- claude_mpm/cli/commands/aggregate.py +7 -3
- claude_mpm/cli/commands/analyze.py +9 -4
- claude_mpm/cli/commands/analyze_code.py +7 -2
- claude_mpm/cli/commands/auto_configure.py +7 -9
- claude_mpm/cli/commands/config.py +47 -13
- claude_mpm/cli/commands/configure.py +294 -1788
- claude_mpm/cli/commands/configure_agent_display.py +261 -0
- claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
- claude_mpm/cli/commands/configure_hook_manager.py +225 -0
- claude_mpm/cli/commands/configure_models.py +18 -0
- claude_mpm/cli/commands/configure_navigation.py +167 -0
- claude_mpm/cli/commands/configure_paths.py +104 -0
- claude_mpm/cli/commands/configure_persistence.py +254 -0
- claude_mpm/cli/commands/configure_startup_manager.py +646 -0
- claude_mpm/cli/commands/configure_template_editor.py +497 -0
- claude_mpm/cli/commands/configure_validators.py +73 -0
- claude_mpm/cli/commands/local_deploy.py +537 -0
- claude_mpm/cli/commands/memory.py +54 -20
- claude_mpm/cli/commands/mpm_init.py +39 -25
- claude_mpm/cli/commands/mpm_init_handler.py +8 -3
- claude_mpm/cli/executor.py +202 -0
- claude_mpm/cli/helpers.py +105 -0
- claude_mpm/cli/interactive/__init__.py +3 -0
- claude_mpm/cli/interactive/skills_wizard.py +491 -0
- claude_mpm/cli/parsers/__init__.py +7 -1
- claude_mpm/cli/parsers/base_parser.py +98 -3
- claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
- claude_mpm/cli/shared/output_formatters.py +28 -19
- claude_mpm/cli/startup.py +481 -0
- claude_mpm/cli/utils.py +52 -1
- claude_mpm/commands/mpm-help.md +3 -0
- claude_mpm/commands/mpm-version.md +113 -0
- claude_mpm/commands/mpm.md +1 -0
- claude_mpm/config/agent_config.py +2 -2
- claude_mpm/config/model_config.py +428 -0
- claude_mpm/core/base_service.py +13 -12
- claude_mpm/core/enums.py +452 -0
- claude_mpm/core/factories.py +1 -1
- claude_mpm/core/instruction_reinforcement_hook.py +2 -1
- claude_mpm/core/interactive_session.py +9 -3
- claude_mpm/core/logging_config.py +6 -2
- claude_mpm/core/oneshot_session.py +8 -4
- claude_mpm/core/optimized_agent_loader.py +3 -3
- claude_mpm/core/output_style_manager.py +12 -192
- claude_mpm/core/service_registry.py +5 -1
- claude_mpm/core/types.py +2 -9
- claude_mpm/core/typing_utils.py +7 -6
- claude_mpm/dashboard/static/js/dashboard.js +0 -14
- claude_mpm/dashboard/templates/index.html +3 -41
- claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
- claude_mpm/hooks/instruction_reinforcement.py +7 -2
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/services/agents/auto_config_manager.py +10 -11
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
- claude_mpm/services/agents/deployment/agent_validator.py +17 -1
- claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
- claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
- claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
- claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
- claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
- claude_mpm/services/agents/local_template_manager.py +1 -1
- claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
- claude_mpm/services/agents/registry/modification_tracker.py +5 -2
- claude_mpm/services/command_handler_service.py +11 -5
- claude_mpm/services/core/interfaces/__init__.py +74 -2
- claude_mpm/services/core/interfaces/health.py +172 -0
- claude_mpm/services/core/interfaces/model.py +281 -0
- claude_mpm/services/core/interfaces/process.py +372 -0
- claude_mpm/services/core/interfaces/restart.py +307 -0
- claude_mpm/services/core/interfaces/stability.py +260 -0
- claude_mpm/services/core/models/__init__.py +33 -0
- claude_mpm/services/core/models/agent_config.py +12 -28
- claude_mpm/services/core/models/health.py +162 -0
- claude_mpm/services/core/models/process.py +235 -0
- claude_mpm/services/core/models/restart.py +302 -0
- claude_mpm/services/core/models/stability.py +264 -0
- claude_mpm/services/core/path_resolver.py +23 -7
- claude_mpm/services/diagnostics/__init__.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
- claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
- claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
- claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
- claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
- claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
- claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +36 -31
- claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
- claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
- claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
- claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
- claude_mpm/services/diagnostics/models.py +19 -24
- claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
- claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
- claude_mpm/services/infrastructure/monitoring/base.py +5 -13
- claude_mpm/services/infrastructure/monitoring/network.py +7 -6
- claude_mpm/services/infrastructure/monitoring/process.py +13 -12
- claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
- claude_mpm/services/infrastructure/monitoring/service.py +16 -15
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/local_ops/__init__.py +163 -0
- claude_mpm/services/local_ops/crash_detector.py +257 -0
- claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
- claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
- claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
- claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
- claude_mpm/services/local_ops/health_manager.py +430 -0
- claude_mpm/services/local_ops/log_monitor.py +396 -0
- claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
- claude_mpm/services/local_ops/process_manager.py +595 -0
- claude_mpm/services/local_ops/resource_monitor.py +331 -0
- claude_mpm/services/local_ops/restart_manager.py +401 -0
- claude_mpm/services/local_ops/restart_policy.py +387 -0
- claude_mpm/services/local_ops/state_manager.py +372 -0
- claude_mpm/services/local_ops/unified_manager.py +600 -0
- claude_mpm/services/mcp_config_manager.py +9 -4
- claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
- claude_mpm/services/mcp_gateway/core/base.py +18 -31
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +71 -24
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
- claude_mpm/services/memory_hook_service.py +4 -1
- claude_mpm/services/model/__init__.py +147 -0
- claude_mpm/services/model/base_provider.py +365 -0
- claude_mpm/services/model/claude_provider.py +412 -0
- claude_mpm/services/model/model_router.py +453 -0
- claude_mpm/services/model/ollama_provider.py +415 -0
- claude_mpm/services/monitor/daemon_manager.py +3 -2
- claude_mpm/services/monitor/handlers/dashboard.py +2 -1
- claude_mpm/services/monitor/handlers/hooks.py +2 -1
- claude_mpm/services/monitor/management/lifecycle.py +3 -2
- claude_mpm/services/monitor/server.py +2 -1
- claude_mpm/services/session_management_service.py +3 -2
- claude_mpm/services/session_manager.py +205 -1
- claude_mpm/services/shared/async_service_base.py +16 -27
- claude_mpm/services/shared/lifecycle_service_base.py +1 -14
- claude_mpm/services/socketio/handlers/__init__.py +5 -2
- claude_mpm/services/socketio/handlers/hook.py +13 -2
- claude_mpm/services/socketio/handlers/registry.py +4 -2
- claude_mpm/services/socketio/server/main.py +10 -8
- claude_mpm/services/subprocess_launcher_service.py +14 -5
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
- claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
- claude_mpm/services/unified/deployment_strategies/local.py +6 -5
- claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
- claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
- claude_mpm/services/unified/interfaces.py +3 -1
- claude_mpm/services/unified/unified_analyzer.py +14 -10
- claude_mpm/services/unified/unified_config.py +2 -1
- claude_mpm/services/unified/unified_deployment.py +9 -4
- claude_mpm/services/version_service.py +104 -1
- claude_mpm/skills/__init__.py +21 -0
- claude_mpm/skills/bundled/__init__.py +6 -0
- claude_mpm/skills/bundled/api-documentation.md +393 -0
- claude_mpm/skills/bundled/async-testing.md +571 -0
- claude_mpm/skills/bundled/code-review.md +143 -0
- claude_mpm/skills/bundled/database-migration.md +199 -0
- claude_mpm/skills/bundled/docker-containerization.md +194 -0
- claude_mpm/skills/bundled/express-local-dev.md +1429 -0
- claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
- claude_mpm/skills/bundled/git-workflow.md +414 -0
- claude_mpm/skills/bundled/imagemagick.md +204 -0
- claude_mpm/skills/bundled/json-data-handling.md +223 -0
- claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
- claude_mpm/skills/bundled/pdf.md +141 -0
- claude_mpm/skills/bundled/performance-profiling.md +567 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/security-scanning.md +327 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/skills/registry.py +286 -0
- claude_mpm/skills/skill_manager.py +310 -0
- claude_mpm/tools/code_tree_analyzer.py +177 -141
- claude_mpm/tools/code_tree_events.py +4 -2
- claude_mpm/utils/agent_dependency_loader.py +2 -2
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +117 -8
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +238 -174
- claude_mpm/dashboard/static/css/code-tree.css +0 -1639
- claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
- claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
- claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
- claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
- claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
- claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
- claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
- claude_mpm/services/project/analyzer_refactored.py +0 -450
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
|
@@ -6,12 +6,19 @@ Ensures a single session ID is generated and used across all components.
|
|
|
6
6
|
|
|
7
7
|
This service addresses race conditions and duplicate session ID generation
|
|
8
8
|
by providing a single source of truth for session identifiers.
|
|
9
|
+
|
|
10
|
+
Extended with:
|
|
11
|
+
- Token usage tracking and monitoring
|
|
12
|
+
- Resume log generation on session end
|
|
13
|
+
- Context metrics persistence
|
|
14
|
+
- Automatic resume log injection on session startup
|
|
9
15
|
"""
|
|
10
16
|
|
|
11
17
|
import os
|
|
12
18
|
from datetime import datetime, timezone
|
|
19
|
+
from pathlib import Path
|
|
13
20
|
from threading import Lock
|
|
14
|
-
from typing import Optional
|
|
21
|
+
from typing import Any, Dict, Optional
|
|
15
22
|
|
|
16
23
|
from claude_mpm.core.logging_utils import get_logger
|
|
17
24
|
|
|
@@ -64,6 +71,24 @@ class SessionManager:
|
|
|
64
71
|
self._session_id = self._generate_session_id()
|
|
65
72
|
self._session_start_time = datetime.now(timezone.utc)
|
|
66
73
|
|
|
74
|
+
# Token usage tracking
|
|
75
|
+
self._cumulative_tokens = 0
|
|
76
|
+
self._total_budget = 200000 # Default Claude Code budget
|
|
77
|
+
self._last_stop_reason: Optional[str] = None
|
|
78
|
+
|
|
79
|
+
# Context metrics storage
|
|
80
|
+
self._context_metrics: Dict[str, Any] = {
|
|
81
|
+
"total_budget": self._total_budget,
|
|
82
|
+
"used_tokens": 0,
|
|
83
|
+
"remaining_tokens": self._total_budget,
|
|
84
|
+
"percentage_used": 0.0,
|
|
85
|
+
"stop_reason": None,
|
|
86
|
+
"model": "claude-sonnet-4.5",
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# Resume log reference (loaded on startup if exists)
|
|
90
|
+
self._resume_log_content: Optional[str] = None
|
|
91
|
+
|
|
67
92
|
# Mark as initialized
|
|
68
93
|
self.__class__._initialized = True
|
|
69
94
|
|
|
@@ -71,6 +96,9 @@ class SessionManager:
|
|
|
71
96
|
f"SessionManager initialized with session ID: {self._session_id}"
|
|
72
97
|
)
|
|
73
98
|
|
|
99
|
+
# Check for resume log from previous session
|
|
100
|
+
self._load_resume_log()
|
|
101
|
+
|
|
74
102
|
def _generate_session_id(self) -> str:
|
|
75
103
|
"""
|
|
76
104
|
Generate or retrieve a session ID.
|
|
@@ -134,6 +162,182 @@ class SessionManager:
|
|
|
134
162
|
f"Session ID already set to {session_id}, no change needed"
|
|
135
163
|
)
|
|
136
164
|
|
|
165
|
+
def update_token_usage(
|
|
166
|
+
self,
|
|
167
|
+
input_tokens: int = 0,
|
|
168
|
+
output_tokens: int = 0,
|
|
169
|
+
stop_reason: Optional[str] = None,
|
|
170
|
+
) -> Dict[str, Any]:
|
|
171
|
+
"""
|
|
172
|
+
Update cumulative token usage for the session.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
input_tokens: Input tokens from latest API call
|
|
176
|
+
output_tokens: Output tokens from latest API call
|
|
177
|
+
stop_reason: Stop reason from Claude API
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Updated context metrics
|
|
181
|
+
"""
|
|
182
|
+
with self.__class__._lock:
|
|
183
|
+
# Update cumulative usage
|
|
184
|
+
tokens_used = input_tokens + output_tokens
|
|
185
|
+
self._cumulative_tokens += tokens_used
|
|
186
|
+
|
|
187
|
+
# Update stop reason if provided
|
|
188
|
+
if stop_reason:
|
|
189
|
+
self._last_stop_reason = stop_reason
|
|
190
|
+
|
|
191
|
+
# Calculate metrics
|
|
192
|
+
remaining = max(0, self._total_budget - self._cumulative_tokens)
|
|
193
|
+
percentage = (self._cumulative_tokens / self._total_budget) * 100
|
|
194
|
+
|
|
195
|
+
# Update context metrics
|
|
196
|
+
self._context_metrics = {
|
|
197
|
+
"total_budget": self._total_budget,
|
|
198
|
+
"used_tokens": self._cumulative_tokens,
|
|
199
|
+
"remaining_tokens": remaining,
|
|
200
|
+
"percentage_used": percentage,
|
|
201
|
+
"stop_reason": self._last_stop_reason,
|
|
202
|
+
"model": "claude-sonnet-4.5",
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
logger.debug(
|
|
206
|
+
f"Token usage updated: {self._cumulative_tokens}/{self._total_budget} "
|
|
207
|
+
f"({percentage:.1f}%) - Stop reason: {stop_reason}"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
return self._context_metrics.copy()
|
|
211
|
+
|
|
212
|
+
def get_context_metrics(self) -> Dict[str, Any]:
|
|
213
|
+
"""
|
|
214
|
+
Get current context metrics.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Dictionary containing token usage and context metrics
|
|
218
|
+
"""
|
|
219
|
+
with self.__class__._lock:
|
|
220
|
+
return self._context_metrics.copy()
|
|
221
|
+
|
|
222
|
+
def get_token_usage_percentage(self) -> float:
|
|
223
|
+
"""
|
|
224
|
+
Get current token usage as a percentage (0.0 to 1.0).
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Token usage percentage
|
|
228
|
+
"""
|
|
229
|
+
with self.__class__._lock:
|
|
230
|
+
return self._context_metrics["percentage_used"] / 100.0
|
|
231
|
+
|
|
232
|
+
def should_warn_context_limit(self, threshold: float = 0.70) -> bool:
|
|
233
|
+
"""
|
|
234
|
+
Check if context usage has reached warning threshold.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
threshold: Warning threshold (0.0 to 1.0)
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
True if threshold reached
|
|
241
|
+
"""
|
|
242
|
+
return self.get_token_usage_percentage() >= threshold
|
|
243
|
+
|
|
244
|
+
def _load_resume_log(self) -> None:
|
|
245
|
+
"""
|
|
246
|
+
Load resume log from previous session if it exists.
|
|
247
|
+
|
|
248
|
+
This is called during initialization to check for session continuity.
|
|
249
|
+
"""
|
|
250
|
+
try:
|
|
251
|
+
# Lazy import to avoid circular dependencies
|
|
252
|
+
from claude_mpm.services.infrastructure.resume_log_generator import (
|
|
253
|
+
ResumeLogGenerator,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
generator = ResumeLogGenerator()
|
|
257
|
+
|
|
258
|
+
# Check if there's a resume log for this session
|
|
259
|
+
# (Could be from a previous interrupted session with same ID)
|
|
260
|
+
resume_content = generator.load_resume_log(self._session_id)
|
|
261
|
+
|
|
262
|
+
if resume_content:
|
|
263
|
+
self._resume_log_content = resume_content
|
|
264
|
+
logger.info(f"Loaded resume log for session {self._session_id}")
|
|
265
|
+
else:
|
|
266
|
+
logger.debug("No resume log found for current session")
|
|
267
|
+
|
|
268
|
+
except Exception as e:
|
|
269
|
+
logger.warning(f"Failed to load resume log: {e}")
|
|
270
|
+
# Non-critical error, continue without resume log
|
|
271
|
+
|
|
272
|
+
def get_resume_log_content(self) -> Optional[str]:
|
|
273
|
+
"""
|
|
274
|
+
Get resume log content if loaded.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
Resume log markdown content or None
|
|
278
|
+
"""
|
|
279
|
+
with self.__class__._lock:
|
|
280
|
+
return self._resume_log_content
|
|
281
|
+
|
|
282
|
+
def generate_resume_log(
|
|
283
|
+
self,
|
|
284
|
+
session_state: Optional[Dict[str, Any]] = None,
|
|
285
|
+
) -> Optional[Path]:
|
|
286
|
+
"""
|
|
287
|
+
Generate and save resume log for current session.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
session_state: Optional session state data to include
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
Path to saved resume log or None if generation failed
|
|
294
|
+
"""
|
|
295
|
+
try:
|
|
296
|
+
# Lazy import to avoid circular dependencies
|
|
297
|
+
from claude_mpm.models.resume_log import ContextMetrics, ResumeLog
|
|
298
|
+
from claude_mpm.services.infrastructure.resume_log_generator import (
|
|
299
|
+
ResumeLogGenerator,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
generator = ResumeLogGenerator()
|
|
303
|
+
|
|
304
|
+
# Create context metrics from current state
|
|
305
|
+
context_metrics = ContextMetrics(
|
|
306
|
+
total_budget=self._total_budget,
|
|
307
|
+
used_tokens=self._cumulative_tokens,
|
|
308
|
+
remaining_tokens=self._context_metrics["remaining_tokens"],
|
|
309
|
+
percentage_used=self._context_metrics["percentage_used"],
|
|
310
|
+
stop_reason=self._last_stop_reason,
|
|
311
|
+
model=self._context_metrics["model"],
|
|
312
|
+
session_id=self._session_id,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
if session_state:
|
|
316
|
+
# Generate from provided session state
|
|
317
|
+
resume_log = generator.generate_from_session_state(
|
|
318
|
+
session_id=self._session_id,
|
|
319
|
+
session_state=session_state,
|
|
320
|
+
stop_reason=self._last_stop_reason,
|
|
321
|
+
)
|
|
322
|
+
else:
|
|
323
|
+
# Create minimal resume log
|
|
324
|
+
resume_log = ResumeLog(
|
|
325
|
+
session_id=self._session_id,
|
|
326
|
+
context_metrics=context_metrics,
|
|
327
|
+
mission_summary="Session ended - resume log auto-generated.",
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
if resume_log:
|
|
331
|
+
file_path = generator.save_resume_log(resume_log)
|
|
332
|
+
logger.info(f"Resume log generated and saved: {file_path}")
|
|
333
|
+
return file_path
|
|
334
|
+
logger.warning("Resume log generation returned None")
|
|
335
|
+
return None
|
|
336
|
+
|
|
337
|
+
except Exception as e:
|
|
338
|
+
logger.error(f"Failed to generate resume log: {e}", exc_info=True)
|
|
339
|
+
return None
|
|
340
|
+
|
|
137
341
|
@classmethod
|
|
138
342
|
def reset(cls) -> None:
|
|
139
343
|
"""
|
|
@@ -4,23 +4,12 @@ Base class for asynchronous services to reduce duplication.
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
from abc import ABC, abstractmethod
|
|
7
|
-
from enum import Enum
|
|
8
7
|
from typing import Any, Dict, Optional
|
|
9
8
|
|
|
9
|
+
from ...core.enums import ServiceState
|
|
10
10
|
from ...core.mixins import LoggerMixin
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class AsyncServiceState(Enum):
|
|
14
|
-
"""Standard states for async services."""
|
|
15
|
-
|
|
16
|
-
UNINITIALIZED = "uninitialized"
|
|
17
|
-
INITIALIZING = "initializing"
|
|
18
|
-
RUNNING = "running"
|
|
19
|
-
STOPPING = "stopping"
|
|
20
|
-
STOPPED = "stopped"
|
|
21
|
-
ERROR = "error"
|
|
22
|
-
|
|
23
|
-
|
|
24
13
|
class AsyncServiceBase(LoggerMixin, ABC):
|
|
25
14
|
"""
|
|
26
15
|
Base class for asynchronous services.
|
|
@@ -45,7 +34,7 @@ class AsyncServiceBase(LoggerMixin, ABC):
|
|
|
45
34
|
self.config = config or {}
|
|
46
35
|
|
|
47
36
|
# State management
|
|
48
|
-
self._state =
|
|
37
|
+
self._state = ServiceState.UNINITIALIZED
|
|
49
38
|
self._state_lock = asyncio.Lock()
|
|
50
39
|
|
|
51
40
|
# Background tasks
|
|
@@ -57,19 +46,19 @@ class AsyncServiceBase(LoggerMixin, ABC):
|
|
|
57
46
|
self._error_count = 0
|
|
58
47
|
|
|
59
48
|
@property
|
|
60
|
-
def state(self) ->
|
|
49
|
+
def state(self) -> ServiceState:
|
|
61
50
|
"""Get current service state."""
|
|
62
51
|
return self._state
|
|
63
52
|
|
|
64
53
|
@property
|
|
65
54
|
def is_running(self) -> bool:
|
|
66
55
|
"""Check if service is running."""
|
|
67
|
-
return self._state ==
|
|
56
|
+
return self._state == ServiceState.RUNNING
|
|
68
57
|
|
|
69
58
|
@property
|
|
70
59
|
def is_healthy(self) -> bool:
|
|
71
60
|
"""Check if service is healthy."""
|
|
72
|
-
return self._state ==
|
|
61
|
+
return self._state == ServiceState.RUNNING and self._last_error is None
|
|
73
62
|
|
|
74
63
|
async def initialize(self) -> bool:
|
|
75
64
|
"""
|
|
@@ -79,22 +68,22 @@ class AsyncServiceBase(LoggerMixin, ABC):
|
|
|
79
68
|
True if initialization successful
|
|
80
69
|
"""
|
|
81
70
|
async with self._state_lock:
|
|
82
|
-
if self._state !=
|
|
71
|
+
if self._state != ServiceState.UNINITIALIZED:
|
|
83
72
|
self.logger.warning(f"Service {self.service_name} already initialized")
|
|
84
|
-
return self._state ==
|
|
73
|
+
return self._state == ServiceState.RUNNING
|
|
85
74
|
|
|
86
|
-
self._state =
|
|
75
|
+
self._state = ServiceState.INITIALIZING
|
|
87
76
|
self.logger.info(f"Initializing service: {self.service_name}")
|
|
88
77
|
|
|
89
78
|
try:
|
|
90
79
|
success = await self._do_initialize()
|
|
91
80
|
if success:
|
|
92
|
-
self._state =
|
|
81
|
+
self._state = ServiceState.RUNNING
|
|
93
82
|
self.logger.info(
|
|
94
83
|
f"Service {self.service_name} initialized successfully"
|
|
95
84
|
)
|
|
96
85
|
else:
|
|
97
|
-
self._state =
|
|
86
|
+
self._state = ServiceState.ERROR
|
|
98
87
|
self.logger.error(
|
|
99
88
|
f"Service {self.service_name} initialization failed"
|
|
100
89
|
)
|
|
@@ -102,7 +91,7 @@ class AsyncServiceBase(LoggerMixin, ABC):
|
|
|
102
91
|
return success
|
|
103
92
|
|
|
104
93
|
except Exception as e:
|
|
105
|
-
self._state =
|
|
94
|
+
self._state = ServiceState.ERROR
|
|
106
95
|
self._last_error = e
|
|
107
96
|
self._error_count += 1
|
|
108
97
|
self.logger.error(
|
|
@@ -114,10 +103,10 @@ class AsyncServiceBase(LoggerMixin, ABC):
|
|
|
114
103
|
async def shutdown(self) -> None:
|
|
115
104
|
"""Shutdown the service gracefully."""
|
|
116
105
|
async with self._state_lock:
|
|
117
|
-
if self._state in (
|
|
106
|
+
if self._state in (ServiceState.STOPPED, ServiceState.STOPPING):
|
|
118
107
|
return
|
|
119
108
|
|
|
120
|
-
self._state =
|
|
109
|
+
self._state = ServiceState.STOPPING
|
|
121
110
|
self.logger.info(f"Shutting down service: {self.service_name}")
|
|
122
111
|
|
|
123
112
|
try:
|
|
@@ -130,11 +119,11 @@ class AsyncServiceBase(LoggerMixin, ABC):
|
|
|
130
119
|
# Service-specific shutdown
|
|
131
120
|
await self._do_shutdown()
|
|
132
121
|
|
|
133
|
-
self._state =
|
|
122
|
+
self._state = ServiceState.STOPPED
|
|
134
123
|
self.logger.info(f"Service {self.service_name} shut down successfully")
|
|
135
124
|
|
|
136
125
|
except Exception as e:
|
|
137
|
-
self._state =
|
|
126
|
+
self._state = ServiceState.ERROR
|
|
138
127
|
self._last_error = e
|
|
139
128
|
self.logger.error(
|
|
140
129
|
f"Service {self.service_name} shutdown error: {e}", exc_info=True
|
|
@@ -146,7 +135,7 @@ class AsyncServiceBase(LoggerMixin, ABC):
|
|
|
146
135
|
await self.shutdown()
|
|
147
136
|
|
|
148
137
|
# Reset state for restart
|
|
149
|
-
self._state =
|
|
138
|
+
self._state = ServiceState.UNINITIALIZED
|
|
150
139
|
self._shutdown_event.clear()
|
|
151
140
|
self._last_error = None
|
|
152
141
|
|
|
@@ -4,25 +4,12 @@ Base class for services with complex lifecycle management.
|
|
|
4
4
|
|
|
5
5
|
import time
|
|
6
6
|
from abc import ABC, abstractmethod
|
|
7
|
-
from enum import Enum
|
|
8
7
|
from typing import Any, Dict, List, Optional
|
|
9
8
|
|
|
9
|
+
from ...core.enums import ServiceState
|
|
10
10
|
from ...core.mixins import LoggerMixin
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class ServiceState(Enum):
|
|
14
|
-
"""Standard service states."""
|
|
15
|
-
|
|
16
|
-
UNINITIALIZED = "uninitialized"
|
|
17
|
-
INITIALIZING = "initializing"
|
|
18
|
-
INITIALIZED = "initialized"
|
|
19
|
-
STARTING = "starting"
|
|
20
|
-
RUNNING = "running"
|
|
21
|
-
STOPPING = "stopping"
|
|
22
|
-
STOPPED = "stopped"
|
|
23
|
-
ERROR = "error"
|
|
24
|
-
|
|
25
|
-
|
|
26
13
|
class LifecycleServiceBase(LoggerMixin, ABC):
|
|
27
14
|
"""
|
|
28
15
|
Base class for services with complex lifecycle management.
|
|
@@ -7,7 +7,9 @@ and maintainability.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from .base import BaseEventHandler
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
# DISABLED: File Tree interface removed from dashboard
|
|
12
|
+
# from .code_analysis import CodeAnalysisEventHandler
|
|
11
13
|
from .connection import ConnectionEventHandler
|
|
12
14
|
from .file import FileEventHandler
|
|
13
15
|
from .git import GitEventHandler
|
|
@@ -17,7 +19,8 @@ from .registry import EventHandlerRegistry
|
|
|
17
19
|
|
|
18
20
|
__all__ = [
|
|
19
21
|
"BaseEventHandler",
|
|
20
|
-
|
|
22
|
+
# DISABLED: File Tree interface removed from dashboard
|
|
23
|
+
# "CodeAnalysisEventHandler",
|
|
21
24
|
"ConnectionEventHandler",
|
|
22
25
|
"EventHandlerRegistry",
|
|
23
26
|
"FileEventHandler",
|
|
@@ -7,6 +7,7 @@ agent delegations, and other hook-based activity for the system heartbeat.
|
|
|
7
7
|
from datetime import datetime, timezone
|
|
8
8
|
from typing import Any, Dict
|
|
9
9
|
|
|
10
|
+
from ....core.enums import ServiceState
|
|
10
11
|
from .base import BaseEventHandler
|
|
11
12
|
|
|
12
13
|
|
|
@@ -60,6 +61,10 @@ class HookEventHandler(BaseEventHandler):
|
|
|
60
61
|
|
|
61
62
|
hook_data = data.get("data", {})
|
|
62
63
|
|
|
64
|
+
# Log hook event processing
|
|
65
|
+
tool_name = hook_data.get("tool_name", "N/A")
|
|
66
|
+
self.logger.info(f"Processing hook event: {hook_event} - tool: {tool_name}")
|
|
67
|
+
|
|
63
68
|
# Create properly formatted event for history
|
|
64
69
|
# Note: add_to_history expects the event data directly, not wrapped
|
|
65
70
|
history_event = {
|
|
@@ -76,6 +81,12 @@ class HookEventHandler(BaseEventHandler):
|
|
|
76
81
|
|
|
77
82
|
# Broadcast the original event to all connected clients
|
|
78
83
|
# (preserves all original fields)
|
|
84
|
+
connected_clients = (
|
|
85
|
+
len(self.server.clients) if hasattr(self.server, "clients") else 0
|
|
86
|
+
)
|
|
87
|
+
self.logger.info(
|
|
88
|
+
f"Broadcasting claude_event to {connected_clients} clients: {hook_event}"
|
|
89
|
+
)
|
|
79
90
|
await self.broadcast_event("claude_event", data)
|
|
80
91
|
|
|
81
92
|
# Track sessions based on hook events
|
|
@@ -108,7 +119,7 @@ class HookEventHandler(BaseEventHandler):
|
|
|
108
119
|
"session_id": session_id,
|
|
109
120
|
"start_time": datetime.now(timezone.utc).isoformat(),
|
|
110
121
|
"agent": agent_type,
|
|
111
|
-
"status":
|
|
122
|
+
"status": ServiceState.RUNNING,
|
|
112
123
|
"prompt": data.get("prompt", "")[:100], # First 100 chars
|
|
113
124
|
"last_activity": datetime.now(timezone.utc).isoformat(),
|
|
114
125
|
}
|
|
@@ -159,7 +170,7 @@ class HookEventHandler(BaseEventHandler):
|
|
|
159
170
|
"session_id": session_id,
|
|
160
171
|
"start_time": datetime.now(timezone.utc).isoformat(),
|
|
161
172
|
"agent": "pm", # Default to PM
|
|
162
|
-
"status":
|
|
173
|
+
"status": ServiceState.RUNNING,
|
|
163
174
|
"prompt": data.get("prompt_text", "")[:100],
|
|
164
175
|
"working_directory": data.get("working_directory", ""),
|
|
165
176
|
"last_activity": datetime.now(timezone.utc).isoformat(),
|
|
@@ -15,7 +15,8 @@ if TYPE_CHECKING:
|
|
|
15
15
|
|
|
16
16
|
from ..server import SocketIOServer
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
# DISABLED: File Tree interface removed from dashboard
|
|
19
|
+
# from .code_analysis import CodeAnalysisEventHandler
|
|
19
20
|
from .connection import ConnectionEventHandler
|
|
20
21
|
from .file import FileEventHandler
|
|
21
22
|
from .git import GitEventHandler
|
|
@@ -38,7 +39,8 @@ class EventHandlerRegistry:
|
|
|
38
39
|
HookEventHandler, # Hook events for session tracking
|
|
39
40
|
GitEventHandler, # Git operations
|
|
40
41
|
FileEventHandler, # File operations
|
|
41
|
-
|
|
42
|
+
# DISABLED: File Tree interface removed from dashboard
|
|
43
|
+
# CodeAnalysisEventHandler, # Code analysis for dashboard
|
|
42
44
|
ProjectEventHandler, # Project management (future)
|
|
43
45
|
MemoryEventHandler, # Memory management (future)
|
|
44
46
|
]
|
|
@@ -16,6 +16,8 @@ from collections import deque
|
|
|
16
16
|
from datetime import datetime, timezone
|
|
17
17
|
from typing import Any, Dict, List, Optional, Set
|
|
18
18
|
|
|
19
|
+
from ....core.enums import ServiceState
|
|
20
|
+
|
|
19
21
|
try:
|
|
20
22
|
import aiohttp
|
|
21
23
|
import socketio
|
|
@@ -265,15 +267,15 @@ class SocketIOServer(SocketIOServiceInterface):
|
|
|
265
267
|
except Exception as e:
|
|
266
268
|
self.logger.error(f"Error during EventBus teardown: {e}")
|
|
267
269
|
|
|
268
|
-
# Stop
|
|
270
|
+
# Stop event handlers
|
|
269
271
|
if self.event_registry:
|
|
270
|
-
from ..handlers import
|
|
271
|
-
|
|
272
|
-
# Stop analysis runner
|
|
273
|
-
analysis_handler = self.event_registry.get_handler(CodeAnalysisEventHandler)
|
|
274
|
-
if analysis_handler and hasattr(analysis_handler, "cleanup"):
|
|
275
|
-
analysis_handler.cleanup()
|
|
272
|
+
from ..handlers import ConnectionEventHandler
|
|
276
273
|
|
|
274
|
+
# DISABLED: File Tree interface removed from dashboard
|
|
275
|
+
# Stop analysis runner (code analysis handler is disabled)
|
|
276
|
+
# analysis_handler = self.event_registry.get_handler(CodeAnalysisEventHandler)
|
|
277
|
+
# if analysis_handler and hasattr(analysis_handler, "cleanup"):
|
|
278
|
+
# analysis_handler.cleanup()
|
|
277
279
|
# Stop health monitoring in connection handler
|
|
278
280
|
conn_handler = self.event_registry.get_handler(ConnectionEventHandler)
|
|
279
281
|
if conn_handler and hasattr(conn_handler, "stop_health_monitoring"):
|
|
@@ -384,7 +386,7 @@ class SocketIOServer(SocketIOServiceInterface):
|
|
|
384
386
|
"session_id": session_id,
|
|
385
387
|
"start_time": datetime.now(timezone.utc).isoformat(),
|
|
386
388
|
"agent": "pm", # Default to PM, will be updated if delegated
|
|
387
|
-
"status":
|
|
389
|
+
"status": ServiceState.RUNNING,
|
|
388
390
|
"launch_method": launch_method,
|
|
389
391
|
"working_dir": working_dir,
|
|
390
392
|
}
|
|
@@ -22,6 +22,7 @@ import tty
|
|
|
22
22
|
from typing import Any, Dict, List, Optional
|
|
23
23
|
|
|
24
24
|
from claude_mpm.core.base_service import BaseService
|
|
25
|
+
from claude_mpm.core.enums import OperationResult, ServiceState
|
|
25
26
|
from claude_mpm.services.core.interfaces import SubprocessLauncherInterface
|
|
26
27
|
|
|
27
28
|
|
|
@@ -62,9 +63,17 @@ class SubprocessLauncherService(BaseService, SubprocessLauncherInterface):
|
|
|
62
63
|
try:
|
|
63
64
|
env = kwargs.get("env", self.prepare_subprocess_environment())
|
|
64
65
|
self.launch_subprocess_interactive(command, env)
|
|
65
|
-
return {
|
|
66
|
+
return {
|
|
67
|
+
"status": OperationResult.SUCCESS,
|
|
68
|
+
"command": command,
|
|
69
|
+
"method": "interactive",
|
|
70
|
+
}
|
|
66
71
|
except Exception as e:
|
|
67
|
-
return {
|
|
72
|
+
return {
|
|
73
|
+
"status": OperationResult.FAILED,
|
|
74
|
+
"error": str(e),
|
|
75
|
+
"command": command,
|
|
76
|
+
}
|
|
68
77
|
|
|
69
78
|
async def launch_subprocess_async(
|
|
70
79
|
self, command: List[str], **kwargs
|
|
@@ -109,7 +118,7 @@ class SubprocessLauncherService(BaseService, SubprocessLauncherInterface):
|
|
|
109
118
|
# For now, return unknown status
|
|
110
119
|
return {
|
|
111
120
|
"process_id": process_id,
|
|
112
|
-
"status":
|
|
121
|
+
"status": OperationResult.UNKNOWN,
|
|
113
122
|
"message": "Process tracking not implemented",
|
|
114
123
|
}
|
|
115
124
|
|
|
@@ -160,7 +169,7 @@ class SubprocessLauncherService(BaseService, SubprocessLauncherInterface):
|
|
|
160
169
|
# Notify WebSocket clients
|
|
161
170
|
if self.websocket_server:
|
|
162
171
|
self.websocket_server.claude_status_changed(
|
|
163
|
-
status=
|
|
172
|
+
status=ServiceState.RUNNING,
|
|
164
173
|
pid=process.pid,
|
|
165
174
|
message="Claude subprocess started",
|
|
166
175
|
)
|
|
@@ -195,7 +204,7 @@ class SubprocessLauncherService(BaseService, SubprocessLauncherInterface):
|
|
|
195
204
|
# Notify WebSocket clients
|
|
196
205
|
if self.websocket_server:
|
|
197
206
|
self.websocket_server.claude_status_changed(
|
|
198
|
-
status=
|
|
207
|
+
status=ServiceState.STOPPED,
|
|
199
208
|
message=f"Claude subprocess exited with code {process.returncode}",
|
|
200
209
|
)
|
|
201
210
|
|
|
@@ -14,6 +14,7 @@ import re
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
from typing import Any, Dict, List, Optional
|
|
16
16
|
|
|
17
|
+
from claude_mpm.core.enums import OperationResult
|
|
17
18
|
from claude_mpm.core.logging_utils import get_logger
|
|
18
19
|
|
|
19
20
|
from ..strategies import (
|
|
@@ -120,7 +121,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
|
|
|
120
121
|
return self._analyze_ast(target, options)
|
|
121
122
|
|
|
122
123
|
return {
|
|
123
|
-
"status":
|
|
124
|
+
"status": OperationResult.ERROR,
|
|
124
125
|
"message": f"Unsupported target type: {type(target).__name__}",
|
|
125
126
|
}
|
|
126
127
|
|
|
@@ -159,7 +160,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
|
|
|
159
160
|
metrics["maintainability_index"] = self._calculate_maintainability(metrics)
|
|
160
161
|
|
|
161
162
|
return {
|
|
162
|
-
"status":
|
|
163
|
+
"status": OperationResult.SUCCESS,
|
|
163
164
|
"type": "file",
|
|
164
165
|
"path": str(file_path),
|
|
165
166
|
"metrics": metrics,
|
|
@@ -168,7 +169,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
|
|
|
168
169
|
except Exception as e:
|
|
169
170
|
logger.error(f"Error analyzing file {file_path}: {e}")
|
|
170
171
|
return {
|
|
171
|
-
"status":
|
|
172
|
+
"status": OperationResult.ERROR,
|
|
172
173
|
"path": str(file_path),
|
|
173
174
|
"error": str(e),
|
|
174
175
|
}
|
|
@@ -178,7 +179,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
|
|
|
178
179
|
) -> Dict[str, Any]:
|
|
179
180
|
"""Analyze all code files in a directory."""
|
|
180
181
|
results = {
|
|
181
|
-
"status":
|
|
182
|
+
"status": OperationResult.SUCCESS,
|
|
182
183
|
"type": "directory",
|
|
183
184
|
"path": str(dir_path),
|
|
184
185
|
"files": [],
|
|
@@ -195,7 +196,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
|
|
|
195
196
|
total_metrics = {}
|
|
196
197
|
for file_path in code_files:
|
|
197
198
|
file_result = self._analyze_file(file_path, options)
|
|
198
|
-
if file_result["status"] ==
|
|
199
|
+
if file_result["status"] == OperationResult.SUCCESS:
|
|
199
200
|
results["files"].append(file_result)
|
|
200
201
|
|
|
201
202
|
# Aggregate metrics
|
|
@@ -285,7 +286,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
|
|
|
285
286
|
)
|
|
286
287
|
|
|
287
288
|
return {
|
|
288
|
-
"status":
|
|
289
|
+
"status": OperationResult.SUCCESS,
|
|
289
290
|
"type": "ast",
|
|
290
291
|
"metrics": metrics,
|
|
291
292
|
}
|
|
@@ -426,7 +427,7 @@ class CodeAnalyzerStrategy(AnalyzerStrategy):
|
|
|
426
427
|
"""Extract key metrics from analysis results."""
|
|
427
428
|
metrics = {}
|
|
428
429
|
|
|
429
|
-
if analysis_result.get("status") !=
|
|
430
|
+
if analysis_result.get("status") != OperationResult.SUCCESS:
|
|
430
431
|
return metrics
|
|
431
432
|
|
|
432
433
|
# Extract relevant metrics
|