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
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"""Resume Log Data Model.
|
|
2
|
+
|
|
3
|
+
This module defines the data structure for session resume logs that enable
|
|
4
|
+
seamless context restoration when Claude hits token limits.
|
|
5
|
+
|
|
6
|
+
Design Philosophy:
|
|
7
|
+
- Target 10k tokens maximum per resume log
|
|
8
|
+
- Human-readable markdown format
|
|
9
|
+
- Structured sections with token budgets
|
|
10
|
+
- Optimized for Claude consumption on session resume
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from datetime import datetime, timezone
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any, Dict, List, Optional
|
|
17
|
+
|
|
18
|
+
from claude_mpm.core.logging_utils import get_logger
|
|
19
|
+
|
|
20
|
+
logger = get_logger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class ContextMetrics:
|
|
25
|
+
"""Context window usage metrics."""
|
|
26
|
+
|
|
27
|
+
total_budget: int = 200000
|
|
28
|
+
used_tokens: int = 0
|
|
29
|
+
remaining_tokens: int = 0
|
|
30
|
+
percentage_used: float = 0.0
|
|
31
|
+
stop_reason: Optional[str] = None
|
|
32
|
+
model: str = "claude-sonnet-4.5"
|
|
33
|
+
session_id: str = ""
|
|
34
|
+
timestamp: str = field(
|
|
35
|
+
default_factory=lambda: datetime.now(timezone.utc).isoformat()
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
39
|
+
"""Convert to dictionary."""
|
|
40
|
+
return {
|
|
41
|
+
"total_budget": self.total_budget,
|
|
42
|
+
"used_tokens": self.used_tokens,
|
|
43
|
+
"remaining_tokens": self.remaining_tokens,
|
|
44
|
+
"percentage_used": self.percentage_used,
|
|
45
|
+
"stop_reason": self.stop_reason,
|
|
46
|
+
"model": self.model,
|
|
47
|
+
"session_id": self.session_id,
|
|
48
|
+
"timestamp": self.timestamp,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ContextMetrics":
|
|
53
|
+
"""Create from dictionary."""
|
|
54
|
+
return cls(
|
|
55
|
+
total_budget=data.get("total_budget", 200000),
|
|
56
|
+
used_tokens=data.get("used_tokens", 0),
|
|
57
|
+
remaining_tokens=data.get("remaining_tokens", 0),
|
|
58
|
+
percentage_used=data.get("percentage_used", 0.0),
|
|
59
|
+
stop_reason=data.get("stop_reason"),
|
|
60
|
+
model=data.get("model", "claude-sonnet-4.5"),
|
|
61
|
+
session_id=data.get("session_id", ""),
|
|
62
|
+
timestamp=data.get("timestamp", datetime.now(timezone.utc).isoformat()),
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass
|
|
67
|
+
class ResumeLog:
|
|
68
|
+
"""Resume log containing all information needed to restore session context.
|
|
69
|
+
|
|
70
|
+
Token Budget Distribution (10k tokens total):
|
|
71
|
+
- Context Metrics: 500 tokens
|
|
72
|
+
- Mission Summary: 1,000 tokens
|
|
73
|
+
- Accomplishments: 2,000 tokens
|
|
74
|
+
- Key Findings: 2,500 tokens
|
|
75
|
+
- Decisions & Rationale: 1,500 tokens
|
|
76
|
+
- Next Steps: 1,500 tokens
|
|
77
|
+
- Critical Context: 1,000 tokens
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
# Session identification
|
|
81
|
+
session_id: str
|
|
82
|
+
previous_session_id: Optional[str] = None
|
|
83
|
+
created_at: str = field(
|
|
84
|
+
default_factory=lambda: datetime.now(timezone.utc).isoformat()
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Context metrics
|
|
88
|
+
context_metrics: ContextMetrics = field(default_factory=ContextMetrics)
|
|
89
|
+
|
|
90
|
+
# Core content sections (with token budgets)
|
|
91
|
+
mission_summary: str = "" # 1,000 tokens - What was the overall goal?
|
|
92
|
+
accomplishments: List[str] = field(
|
|
93
|
+
default_factory=list
|
|
94
|
+
) # 2,000 tokens - What was completed?
|
|
95
|
+
key_findings: List[str] = field(
|
|
96
|
+
default_factory=list
|
|
97
|
+
) # 2,500 tokens - What was discovered?
|
|
98
|
+
decisions_made: List[Dict[str, str]] = field(
|
|
99
|
+
default_factory=list
|
|
100
|
+
) # 1,500 tokens - What choices were made and why?
|
|
101
|
+
next_steps: List[str] = field(
|
|
102
|
+
default_factory=list
|
|
103
|
+
) # 1,500 tokens - What needs to happen next?
|
|
104
|
+
critical_context: Dict[str, Any] = field(
|
|
105
|
+
default_factory=dict
|
|
106
|
+
) # 1,000 tokens - Essential state/data
|
|
107
|
+
|
|
108
|
+
# Metadata
|
|
109
|
+
files_modified: List[str] = field(default_factory=list)
|
|
110
|
+
agents_used: Dict[str, int] = field(default_factory=dict)
|
|
111
|
+
errors_encountered: List[str] = field(default_factory=list)
|
|
112
|
+
warnings: List[str] = field(default_factory=list)
|
|
113
|
+
|
|
114
|
+
def to_markdown(self) -> str:
|
|
115
|
+
"""Generate markdown format for Claude consumption.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Markdown-formatted resume log
|
|
119
|
+
"""
|
|
120
|
+
sections = []
|
|
121
|
+
|
|
122
|
+
# Header
|
|
123
|
+
sections.append(f"# Session Resume Log: {self.session_id}\n")
|
|
124
|
+
sections.append(f"**Created**: {self.created_at}")
|
|
125
|
+
if self.previous_session_id:
|
|
126
|
+
sections.append(f"**Previous Session**: {self.previous_session_id}")
|
|
127
|
+
sections.append("")
|
|
128
|
+
|
|
129
|
+
# Context Metrics (500 tokens)
|
|
130
|
+
sections.append("## Context Metrics\n")
|
|
131
|
+
sections.append(f"- **Model**: {self.context_metrics.model}")
|
|
132
|
+
sections.append(
|
|
133
|
+
f"- **Tokens Used**: {self.context_metrics.used_tokens:,} / {self.context_metrics.total_budget:,}"
|
|
134
|
+
)
|
|
135
|
+
sections.append(
|
|
136
|
+
f"- **Percentage**: {self.context_metrics.percentage_used:.1f}%"
|
|
137
|
+
)
|
|
138
|
+
sections.append(
|
|
139
|
+
f"- **Remaining**: {self.context_metrics.remaining_tokens:,} tokens"
|
|
140
|
+
)
|
|
141
|
+
if self.context_metrics.stop_reason:
|
|
142
|
+
sections.append(f"- **Stop Reason**: {self.context_metrics.stop_reason}")
|
|
143
|
+
sections.append("")
|
|
144
|
+
|
|
145
|
+
# Mission Summary (1,000 tokens)
|
|
146
|
+
sections.append("## Mission Summary\n")
|
|
147
|
+
sections.append(
|
|
148
|
+
self.mission_summary
|
|
149
|
+
if self.mission_summary
|
|
150
|
+
else "_No mission summary provided_"
|
|
151
|
+
)
|
|
152
|
+
sections.append("")
|
|
153
|
+
|
|
154
|
+
# Accomplishments (2,000 tokens)
|
|
155
|
+
sections.append("## Accomplishments\n")
|
|
156
|
+
if self.accomplishments:
|
|
157
|
+
for i, item in enumerate(self.accomplishments, 1):
|
|
158
|
+
sections.append(f"{i}. {item}")
|
|
159
|
+
else:
|
|
160
|
+
sections.append("_No accomplishments recorded_")
|
|
161
|
+
sections.append("")
|
|
162
|
+
|
|
163
|
+
# Key Findings (2,500 tokens)
|
|
164
|
+
sections.append("## Key Findings\n")
|
|
165
|
+
if self.key_findings:
|
|
166
|
+
for i, finding in enumerate(self.key_findings, 1):
|
|
167
|
+
sections.append(f"{i}. {finding}")
|
|
168
|
+
else:
|
|
169
|
+
sections.append("_No key findings recorded_")
|
|
170
|
+
sections.append("")
|
|
171
|
+
|
|
172
|
+
# Decisions & Rationale (1,500 tokens)
|
|
173
|
+
sections.append("## Decisions & Rationale\n")
|
|
174
|
+
if self.decisions_made:
|
|
175
|
+
for i, decision in enumerate(self.decisions_made, 1):
|
|
176
|
+
decision_text = decision.get("decision", "")
|
|
177
|
+
rationale = decision.get("rationale", "")
|
|
178
|
+
sections.append(f"{i}. **Decision**: {decision_text}")
|
|
179
|
+
if rationale:
|
|
180
|
+
sections.append(f" **Rationale**: {rationale}")
|
|
181
|
+
else:
|
|
182
|
+
sections.append("_No decisions recorded_")
|
|
183
|
+
sections.append("")
|
|
184
|
+
|
|
185
|
+
# Next Steps (1,500 tokens)
|
|
186
|
+
sections.append("## Next Steps\n")
|
|
187
|
+
if self.next_steps:
|
|
188
|
+
for i, step in enumerate(self.next_steps, 1):
|
|
189
|
+
sections.append(f"{i}. {step}")
|
|
190
|
+
else:
|
|
191
|
+
sections.append("_No next steps defined_")
|
|
192
|
+
sections.append("")
|
|
193
|
+
|
|
194
|
+
# Critical Context (1,000 tokens)
|
|
195
|
+
sections.append("## Critical Context\n")
|
|
196
|
+
if self.critical_context:
|
|
197
|
+
for key, value in self.critical_context.items():
|
|
198
|
+
sections.append(f"- **{key}**: {value}")
|
|
199
|
+
else:
|
|
200
|
+
sections.append("_No critical context preserved_")
|
|
201
|
+
sections.append("")
|
|
202
|
+
|
|
203
|
+
# Metadata
|
|
204
|
+
sections.append("## Session Metadata\n")
|
|
205
|
+
if self.files_modified:
|
|
206
|
+
sections.append(f"**Files Modified** ({len(self.files_modified)}):")
|
|
207
|
+
for file in self.files_modified[:20]: # Limit to first 20
|
|
208
|
+
sections.append(f"- {file}")
|
|
209
|
+
if len(self.files_modified) > 20:
|
|
210
|
+
sections.append(f"- ... and {len(self.files_modified) - 20} more")
|
|
211
|
+
sections.append("")
|
|
212
|
+
|
|
213
|
+
if self.agents_used:
|
|
214
|
+
sections.append("**Agents Used**:")
|
|
215
|
+
for agent, count in self.agents_used.items():
|
|
216
|
+
sections.append(f"- {agent}: {count} delegations")
|
|
217
|
+
sections.append("")
|
|
218
|
+
|
|
219
|
+
if self.errors_encountered:
|
|
220
|
+
sections.append(f"**Errors** ({len(self.errors_encountered)}):")
|
|
221
|
+
for error in self.errors_encountered[:5]: # Limit to first 5
|
|
222
|
+
sections.append(f"- {error}")
|
|
223
|
+
sections.append("")
|
|
224
|
+
|
|
225
|
+
if self.warnings:
|
|
226
|
+
sections.append(f"**Warnings** ({len(self.warnings)}):")
|
|
227
|
+
for warning in self.warnings[:5]: # Limit to first 5
|
|
228
|
+
sections.append(f"- {warning}")
|
|
229
|
+
sections.append("")
|
|
230
|
+
|
|
231
|
+
return "\n".join(sections)
|
|
232
|
+
|
|
233
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
234
|
+
"""Convert to dictionary for JSON serialization."""
|
|
235
|
+
return {
|
|
236
|
+
"session_id": self.session_id,
|
|
237
|
+
"previous_session_id": self.previous_session_id,
|
|
238
|
+
"created_at": self.created_at,
|
|
239
|
+
"context_metrics": self.context_metrics.to_dict(),
|
|
240
|
+
"mission_summary": self.mission_summary,
|
|
241
|
+
"accomplishments": self.accomplishments,
|
|
242
|
+
"key_findings": self.key_findings,
|
|
243
|
+
"decisions_made": self.decisions_made,
|
|
244
|
+
"next_steps": self.next_steps,
|
|
245
|
+
"critical_context": self.critical_context,
|
|
246
|
+
"files_modified": self.files_modified,
|
|
247
|
+
"agents_used": self.agents_used,
|
|
248
|
+
"errors_encountered": self.errors_encountered,
|
|
249
|
+
"warnings": self.warnings,
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
@classmethod
|
|
253
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ResumeLog":
|
|
254
|
+
"""Create from dictionary."""
|
|
255
|
+
context_metrics_data = data.get("context_metrics", {})
|
|
256
|
+
context_metrics = ContextMetrics.from_dict(context_metrics_data)
|
|
257
|
+
|
|
258
|
+
return cls(
|
|
259
|
+
session_id=data.get("session_id", ""),
|
|
260
|
+
previous_session_id=data.get("previous_session_id"),
|
|
261
|
+
created_at=data.get("created_at", datetime.now(timezone.utc).isoformat()),
|
|
262
|
+
context_metrics=context_metrics,
|
|
263
|
+
mission_summary=data.get("mission_summary", ""),
|
|
264
|
+
accomplishments=data.get("accomplishments", []),
|
|
265
|
+
key_findings=data.get("key_findings", []),
|
|
266
|
+
decisions_made=data.get("decisions_made", []),
|
|
267
|
+
next_steps=data.get("next_steps", []),
|
|
268
|
+
critical_context=data.get("critical_context", {}),
|
|
269
|
+
files_modified=data.get("files_modified", []),
|
|
270
|
+
agents_used=data.get("agents_used", {}),
|
|
271
|
+
errors_encountered=data.get("errors_encountered", []),
|
|
272
|
+
warnings=data.get("warnings", []),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
def save(self, storage_dir: Optional[Path] = None) -> Path:
|
|
276
|
+
"""Save resume log to markdown file.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
storage_dir: Directory to save the log (default: .claude-mpm/resume-logs)
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
Path to saved file
|
|
283
|
+
"""
|
|
284
|
+
if storage_dir is None:
|
|
285
|
+
storage_dir = Path.home() / ".claude-mpm" / "resume-logs"
|
|
286
|
+
|
|
287
|
+
storage_dir.mkdir(parents=True, exist_ok=True)
|
|
288
|
+
|
|
289
|
+
# Generate filename
|
|
290
|
+
file_path = storage_dir / f"session-{self.session_id}.md"
|
|
291
|
+
|
|
292
|
+
try:
|
|
293
|
+
# Write markdown file
|
|
294
|
+
markdown_content = self.to_markdown()
|
|
295
|
+
file_path.write_text(markdown_content, encoding="utf-8")
|
|
296
|
+
|
|
297
|
+
logger.info(f"Resume log saved: {file_path}")
|
|
298
|
+
return file_path
|
|
299
|
+
|
|
300
|
+
except Exception as e:
|
|
301
|
+
logger.error(f"Failed to save resume log: {e}")
|
|
302
|
+
raise
|
|
303
|
+
|
|
304
|
+
@classmethod
|
|
305
|
+
def load(
|
|
306
|
+
cls, session_id: str, storage_dir: Optional[Path] = None
|
|
307
|
+
) -> Optional["ResumeLog"]:
|
|
308
|
+
"""Load resume log from file.
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
session_id: Session ID to load
|
|
312
|
+
storage_dir: Directory to load from (default: .claude-mpm/resume-logs)
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
ResumeLog instance or None if not found
|
|
316
|
+
"""
|
|
317
|
+
if storage_dir is None:
|
|
318
|
+
storage_dir = Path.home() / ".claude-mpm" / "resume-logs"
|
|
319
|
+
|
|
320
|
+
file_path = storage_dir / f"session-{session_id}.md"
|
|
321
|
+
|
|
322
|
+
if not file_path.exists():
|
|
323
|
+
logger.debug(f"Resume log not found: {file_path}")
|
|
324
|
+
return None
|
|
325
|
+
|
|
326
|
+
try:
|
|
327
|
+
# For now, we just return the markdown content
|
|
328
|
+
# In the future, could parse markdown back to structured data
|
|
329
|
+
_ = file_path.read_text(encoding="utf-8")
|
|
330
|
+
logger.info(f"Resume log loaded: {file_path}")
|
|
331
|
+
|
|
332
|
+
# Return a basic ResumeLog with the markdown content embedded
|
|
333
|
+
return cls(
|
|
334
|
+
session_id=session_id,
|
|
335
|
+
mission_summary=f"Loaded from previous session. See full context in {file_path}",
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
except Exception as e:
|
|
339
|
+
logger.error(f"Failed to load resume log: {e}")
|
|
340
|
+
return None
|
|
@@ -23,15 +23,14 @@ from typing import Any, Dict, List, Optional
|
|
|
23
23
|
import yaml
|
|
24
24
|
|
|
25
25
|
from ...core.base_service import BaseService
|
|
26
|
+
from ...core.enums import OperationResult, ValidationSeverity
|
|
26
27
|
from ..core.interfaces.agent import IAgentRegistry, IAutoConfigManager
|
|
27
28
|
from ..core.models.agent_config import (
|
|
28
29
|
AgentRecommendation,
|
|
29
30
|
ConfigurationPreview,
|
|
30
31
|
ConfigurationResult,
|
|
31
|
-
ConfigurationStatus,
|
|
32
32
|
ValidationIssue,
|
|
33
33
|
ValidationResult,
|
|
34
|
-
ValidationSeverity,
|
|
35
34
|
)
|
|
36
35
|
from ..core.models.toolchain import ToolchainAnalysis
|
|
37
36
|
from .observers import IDeploymentObserver, NullObserver
|
|
@@ -216,7 +215,7 @@ class AutoConfigManagerService(BaseService, IAutoConfigManager):
|
|
|
216
215
|
|
|
217
216
|
if not recommendations:
|
|
218
217
|
return ConfigurationResult(
|
|
219
|
-
status=
|
|
218
|
+
status=OperationResult.SUCCESS,
|
|
220
219
|
message="No agents recommended for this project configuration",
|
|
221
220
|
recommendations=recommendations,
|
|
222
221
|
metadata={
|
|
@@ -241,7 +240,7 @@ class AutoConfigManagerService(BaseService, IAutoConfigManager):
|
|
|
241
240
|
f"Validation failed with {validation_result.error_count} errors"
|
|
242
241
|
)
|
|
243
242
|
return ConfigurationResult(
|
|
244
|
-
status=
|
|
243
|
+
status=OperationResult.ERROR,
|
|
245
244
|
validation_errors=[
|
|
246
245
|
issue.message for issue in validation_result.errors
|
|
247
246
|
],
|
|
@@ -260,7 +259,7 @@ class AutoConfigManagerService(BaseService, IAutoConfigManager):
|
|
|
260
259
|
if dry_run:
|
|
261
260
|
self.logger.info("Dry-run mode: skipping deployment")
|
|
262
261
|
return ConfigurationResult(
|
|
263
|
-
status=
|
|
262
|
+
status=OperationResult.SUCCESS,
|
|
264
263
|
validation_warnings=[
|
|
265
264
|
issue.message for issue in validation_result.warnings
|
|
266
265
|
],
|
|
@@ -280,7 +279,7 @@ class AutoConfigManagerService(BaseService, IAutoConfigManager):
|
|
|
280
279
|
if not confirmed:
|
|
281
280
|
self.logger.info("User cancelled auto-configuration")
|
|
282
281
|
return ConfigurationResult(
|
|
283
|
-
status=
|
|
282
|
+
status=OperationResult.CANCELLED,
|
|
284
283
|
recommendations=recommendations,
|
|
285
284
|
message="Auto-configuration cancelled by user",
|
|
286
285
|
metadata={
|
|
@@ -318,9 +317,9 @@ class AutoConfigManagerService(BaseService, IAutoConfigManager):
|
|
|
318
317
|
|
|
319
318
|
return ConfigurationResult(
|
|
320
319
|
status=(
|
|
321
|
-
|
|
320
|
+
OperationResult.WARNING
|
|
322
321
|
if deployed_agents
|
|
323
|
-
else
|
|
322
|
+
else OperationResult.FAILED
|
|
324
323
|
),
|
|
325
324
|
deployed_agents=deployed_agents,
|
|
326
325
|
failed_agents=failed_agents,
|
|
@@ -347,7 +346,7 @@ class AutoConfigManagerService(BaseService, IAutoConfigManager):
|
|
|
347
346
|
)
|
|
348
347
|
|
|
349
348
|
return ConfigurationResult(
|
|
350
|
-
status=
|
|
349
|
+
status=OperationResult.SUCCESS,
|
|
351
350
|
deployed_agents=deployed_agents,
|
|
352
351
|
validation_warnings=[
|
|
353
352
|
issue.message for issue in validation_result.warnings
|
|
@@ -365,7 +364,7 @@ class AutoConfigManagerService(BaseService, IAutoConfigManager):
|
|
|
365
364
|
observer.on_error("auto-configuration", str(e), e)
|
|
366
365
|
|
|
367
366
|
return ConfigurationResult(
|
|
368
|
-
status=
|
|
367
|
+
status=OperationResult.FAILED,
|
|
369
368
|
message=f"Auto-configuration failed: {e}",
|
|
370
369
|
metadata={
|
|
371
370
|
"duration_ms": (time.time() - start_time) * 1000,
|
|
@@ -679,7 +678,7 @@ class AutoConfigManagerService(BaseService, IAutoConfigManager):
|
|
|
679
678
|
agent_id, agent_name, success=True
|
|
680
679
|
)
|
|
681
680
|
deployed.append(agent_id)
|
|
682
|
-
self.logger.
|
|
681
|
+
self.logger.debug(f"Successfully deployed agent: {agent_id}")
|
|
683
682
|
|
|
684
683
|
except Exception as e:
|
|
685
684
|
self.logger.error(
|
|
@@ -71,7 +71,7 @@ class AgentConfigurationManager:
|
|
|
71
71
|
# Cache the result
|
|
72
72
|
self._base_agent_cache = (base_agent_data, base_agent_version)
|
|
73
73
|
|
|
74
|
-
self.logger.
|
|
74
|
+
self.logger.debug(f"Loaded base agent from {self.base_agent_path}")
|
|
75
75
|
return self._base_agent_cache
|
|
76
76
|
|
|
77
77
|
except Exception as e:
|
|
@@ -107,7 +107,7 @@ class AgentRecordService(BaseService):
|
|
|
107
107
|
record = self._deserialize_record(record_data)
|
|
108
108
|
records[agent_name] = record
|
|
109
109
|
|
|
110
|
-
self.logger.
|
|
110
|
+
self.logger.debug(f"Loaded {len(records)} agent records")
|
|
111
111
|
else:
|
|
112
112
|
self.logger.debug("No existing records file found")
|
|
113
113
|
|
|
@@ -329,10 +329,26 @@ class AgentValidator:
|
|
|
329
329
|
"type": "agent", # Default type
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
# Extract from YAML frontmatter
|
|
332
|
+
# Extract ONLY from YAML frontmatter (between --- markers)
|
|
333
333
|
lines = content.split("\n")
|
|
334
|
+
in_frontmatter = False
|
|
335
|
+
frontmatter_ended = False
|
|
336
|
+
|
|
334
337
|
for line in lines:
|
|
335
338
|
stripped_line = line.strip()
|
|
339
|
+
|
|
340
|
+
# Track frontmatter boundaries
|
|
341
|
+
if stripped_line == "---":
|
|
342
|
+
if not in_frontmatter:
|
|
343
|
+
in_frontmatter = True
|
|
344
|
+
continue
|
|
345
|
+
frontmatter_ended = True
|
|
346
|
+
break # Stop parsing after frontmatter ends
|
|
347
|
+
|
|
348
|
+
# Only parse within frontmatter
|
|
349
|
+
if not in_frontmatter or frontmatter_ended:
|
|
350
|
+
continue
|
|
351
|
+
|
|
336
352
|
if stripped_line.startswith("name:"):
|
|
337
353
|
agent_info["name"] = stripped_line.split(":", 1)[1].strip().strip("\"'")
|
|
338
354
|
elif stripped_line.startswith("description:"):
|
|
@@ -224,7 +224,7 @@ class AsyncAgentDeploymentService:
|
|
|
224
224
|
|
|
225
225
|
elapsed = (time.time() - start_time) * 1000
|
|
226
226
|
self._metrics["time_saved_ms"] += max(0, (len(directories) * 75) - elapsed)
|
|
227
|
-
self.logger.
|
|
227
|
+
self.logger.debug(f"Discovered agents in {elapsed:.1f}ms (parallel scan)")
|
|
228
228
|
|
|
229
229
|
return discovered
|
|
230
230
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any, Dict, List, Tuple
|
|
5
5
|
|
|
6
|
+
from claude_mpm.core.enums import OperationResult
|
|
6
7
|
from claude_mpm.core.interfaces import AgentDeploymentInterface
|
|
7
8
|
from claude_mpm.core.logger import get_logger
|
|
8
9
|
|
|
@@ -195,7 +196,7 @@ class AgentDeploymentInterfaceAdapter(AgentDeploymentInterface):
|
|
|
195
196
|
# Ensure the result is a dictionary
|
|
196
197
|
if not isinstance(status, dict):
|
|
197
198
|
return {
|
|
198
|
-
"status":
|
|
199
|
+
"status": OperationResult.UNKNOWN,
|
|
199
200
|
"error": "Invalid status format from deployment service",
|
|
200
201
|
"interface_version": "1.0.0",
|
|
201
202
|
"adapter_used": True,
|
|
@@ -210,7 +211,7 @@ class AgentDeploymentInterfaceAdapter(AgentDeploymentInterface):
|
|
|
210
211
|
except Exception as e:
|
|
211
212
|
self.logger.error(f"Failed to get deployment status: {e}", exc_info=True)
|
|
212
213
|
return {
|
|
213
|
-
"status":
|
|
214
|
+
"status": OperationResult.ERROR,
|
|
214
215
|
"error": str(e),
|
|
215
216
|
"interface_version": "1.0.0",
|
|
216
217
|
"adapter_used": True,
|
|
@@ -90,7 +90,7 @@ class LocalTemplateDeploymentService:
|
|
|
90
90
|
logger.error(f"Failed to deploy local template {agent_id}: {e}")
|
|
91
91
|
results["errors"].append(f"{agent_id}: {e}")
|
|
92
92
|
|
|
93
|
-
logger.
|
|
93
|
+
logger.debug(
|
|
94
94
|
f"Local template deployment: deployed={len(results['deployed'])}, "
|
|
95
95
|
f"updated={len(results['updated'])}, skipped={len(results['skipped'])}, "
|
|
96
96
|
f"errors={len(results['errors'])}"
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
import time
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
+
from claude_mpm.core.enums import OperationResult
|
|
6
7
|
from claude_mpm.services.agents.deployment.processors import (
|
|
7
8
|
AgentDeploymentContext,
|
|
8
9
|
AgentDeploymentResult,
|
|
9
10
|
AgentProcessor,
|
|
10
11
|
)
|
|
11
12
|
|
|
12
|
-
from .base_step import BaseDeploymentStep, StepResult
|
|
13
|
+
from .base_step import BaseDeploymentStep, StepResult
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class AgentProcessingStep(BaseDeploymentStep):
|
|
@@ -37,7 +38,7 @@ class AgentProcessingStep(BaseDeploymentStep):
|
|
|
37
38
|
if not context.template_files:
|
|
38
39
|
self.logger.warning("No template files to process")
|
|
39
40
|
return StepResult(
|
|
40
|
-
status=
|
|
41
|
+
status=OperationResult.SKIPPED,
|
|
41
42
|
message="No template files found to process",
|
|
42
43
|
execution_time=time.time() - start_time,
|
|
43
44
|
)
|
|
@@ -103,13 +104,13 @@ class AgentProcessingStep(BaseDeploymentStep):
|
|
|
103
104
|
|
|
104
105
|
# Determine step status
|
|
105
106
|
if failed_count == 0:
|
|
106
|
-
status =
|
|
107
|
+
status = OperationResult.SUCCESS
|
|
107
108
|
message = f"Successfully processed {processed_count} agents in {execution_time:.3f}s"
|
|
108
109
|
elif processed_count > 0:
|
|
109
|
-
status =
|
|
110
|
+
status = OperationResult.WARNING
|
|
110
111
|
message = f"Processed {processed_count} agents with {failed_count} failures in {execution_time:.3f}s"
|
|
111
112
|
else:
|
|
112
|
-
status =
|
|
113
|
+
status = OperationResult.FAILED
|
|
113
114
|
message = f"Failed to process any agents ({failed_count} failures) in {execution_time:.3f}s"
|
|
114
115
|
|
|
115
116
|
self.logger.info(message)
|
|
@@ -127,7 +128,7 @@ class AgentProcessingStep(BaseDeploymentStep):
|
|
|
127
128
|
context.add_error(error_msg)
|
|
128
129
|
|
|
129
130
|
return StepResult(
|
|
130
|
-
status=
|
|
131
|
+
status=OperationResult.FAILED,
|
|
131
132
|
message=error_msg,
|
|
132
133
|
error=e,
|
|
133
134
|
execution_time=execution_time,
|
|
@@ -2,26 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
-
from enum import Enum
|
|
6
5
|
from typing import Optional
|
|
7
6
|
|
|
7
|
+
from claude_mpm.core.enums import OperationResult
|
|
8
8
|
from claude_mpm.core.logger import get_logger
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class StepStatus(Enum):
|
|
12
|
-
"""Status of a pipeline step execution."""
|
|
13
|
-
|
|
14
|
-
SUCCESS = "success"
|
|
15
|
-
FAILURE = "failure"
|
|
16
|
-
SKIPPED = "skipped"
|
|
17
|
-
WARNING = "warning"
|
|
18
|
-
|
|
19
|
-
|
|
20
11
|
@dataclass
|
|
21
12
|
class StepResult:
|
|
22
13
|
"""Result of executing a pipeline step."""
|
|
23
14
|
|
|
24
|
-
status:
|
|
15
|
+
status: OperationResult
|
|
25
16
|
message: Optional[str] = None
|
|
26
17
|
error: Optional[Exception] = None
|
|
27
18
|
execution_time: Optional[float] = None
|
|
@@ -29,22 +20,22 @@ class StepResult:
|
|
|
29
20
|
@property
|
|
30
21
|
def is_success(self) -> bool:
|
|
31
22
|
"""Check if the step was successful."""
|
|
32
|
-
return self.status ==
|
|
23
|
+
return self.status == OperationResult.SUCCESS
|
|
33
24
|
|
|
34
25
|
@property
|
|
35
26
|
def is_failure(self) -> bool:
|
|
36
27
|
"""Check if the step failed."""
|
|
37
|
-
return self.status ==
|
|
28
|
+
return self.status == OperationResult.FAILED
|
|
38
29
|
|
|
39
30
|
@property
|
|
40
31
|
def is_skipped(self) -> bool:
|
|
41
32
|
"""Check if the step was skipped."""
|
|
42
|
-
return self.status ==
|
|
33
|
+
return self.status == OperationResult.SKIPPED
|
|
43
34
|
|
|
44
35
|
@property
|
|
45
36
|
def is_warning(self) -> bool:
|
|
46
37
|
"""Check if the step completed with warnings."""
|
|
47
|
-
return self.status ==
|
|
38
|
+
return self.status == OperationResult.WARNING
|
|
48
39
|
|
|
49
40
|
|
|
50
41
|
class BaseDeploymentStep(ABC):
|
|
@@ -67,7 +58,7 @@ class BaseDeploymentStep(ABC):
|
|
|
67
58
|
self.logger = get_logger(f"{__name__}.{self.__class__.__name__}")
|
|
68
59
|
|
|
69
60
|
@abstractmethod
|
|
70
|
-
def execute(self, context) -> StepResult:
|
|
61
|
+
def execute(self, context) -> "StepResult":
|
|
71
62
|
"""Execute this deployment step.
|
|
72
63
|
|
|
73
64
|
Args:
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
import time
|
|
4
4
|
|
|
5
5
|
from claude_mpm.core.config import Config
|
|
6
|
+
from claude_mpm.core.enums import OperationResult
|
|
6
7
|
|
|
7
|
-
from .base_step import BaseDeploymentStep, StepResult
|
|
8
|
+
from .base_step import BaseDeploymentStep, StepResult
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class ConfigurationLoadStep(BaseDeploymentStep):
|
|
@@ -54,7 +55,7 @@ class ConfigurationLoadStep(BaseDeploymentStep):
|
|
|
54
55
|
context.step_timings[self.name] = execution_time
|
|
55
56
|
|
|
56
57
|
return StepResult(
|
|
57
|
-
status=
|
|
58
|
+
status=OperationResult.SUCCESS,
|
|
58
59
|
message=f"Configuration loaded successfully in {execution_time:.3f}s",
|
|
59
60
|
execution_time=execution_time,
|
|
60
61
|
)
|
|
@@ -68,7 +69,7 @@ class ConfigurationLoadStep(BaseDeploymentStep):
|
|
|
68
69
|
context.add_error(error_msg)
|
|
69
70
|
|
|
70
71
|
return StepResult(
|
|
71
|
-
status=
|
|
72
|
+
status=OperationResult.FAILED,
|
|
72
73
|
message=error_msg,
|
|
73
74
|
error=e,
|
|
74
75
|
execution_time=execution_time,
|