claude-mpm 4.1.2__py3-none-any.whl → 4.1.4__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_AGENT_TEMPLATE.md +16 -19
- claude_mpm/agents/MEMORY.md +21 -49
- claude_mpm/agents/templates/OPTIMIZATION_REPORT.md +156 -0
- claude_mpm/agents/templates/api_qa.json +36 -116
- claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json +42 -9
- claude_mpm/agents/templates/backup/documentation_agent_20250726_234551.json +29 -6
- claude_mpm/agents/templates/backup/engineer_agent_20250726_234551.json +34 -6
- claude_mpm/agents/templates/backup/ops_agent_20250726_234551.json +41 -9
- claude_mpm/agents/templates/backup/qa_agent_20250726_234551.json +30 -8
- claude_mpm/agents/templates/backup/research_agent_2025011_234551.json +2 -2
- claude_mpm/agents/templates/backup/research_agent_20250726_234551.json +29 -6
- claude_mpm/agents/templates/backup/research_memory_efficient.json +2 -2
- claude_mpm/agents/templates/backup/security_agent_20250726_234551.json +41 -9
- claude_mpm/agents/templates/backup/version_control_agent_20250726_234551.json +23 -7
- claude_mpm/agents/templates/code_analyzer.json +18 -36
- claude_mpm/agents/templates/data_engineer.json +43 -14
- claude_mpm/agents/templates/documentation.json +55 -74
- claude_mpm/agents/templates/engineer.json +57 -40
- claude_mpm/agents/templates/imagemagick.json +7 -2
- claude_mpm/agents/templates/memory_manager.json +1 -1
- claude_mpm/agents/templates/ops.json +36 -4
- claude_mpm/agents/templates/project_organizer.json +23 -71
- claude_mpm/agents/templates/qa.json +34 -2
- claude_mpm/agents/templates/refactoring_engineer.json +9 -5
- claude_mpm/agents/templates/research.json +36 -4
- claude_mpm/agents/templates/security.json +29 -2
- claude_mpm/agents/templates/ticketing.json +3 -3
- claude_mpm/agents/templates/vercel_ops_agent.json +2 -2
- claude_mpm/agents/templates/version_control.json +28 -2
- claude_mpm/agents/templates/web_qa.json +38 -151
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/commands/agent_manager.py +221 -1
- claude_mpm/cli/commands/agents.py +556 -1009
- claude_mpm/cli/commands/memory.py +248 -927
- claude_mpm/cli/commands/run.py +139 -484
- claude_mpm/cli/parsers/agent_manager_parser.py +34 -0
- claude_mpm/cli/startup_logging.py +76 -0
- claude_mpm/core/agent_registry.py +6 -10
- claude_mpm/core/framework_loader.py +205 -595
- claude_mpm/core/log_manager.py +49 -1
- claude_mpm/core/logging_config.py +2 -4
- claude_mpm/hooks/claude_hooks/event_handlers.py +7 -117
- claude_mpm/hooks/claude_hooks/hook_handler.py +91 -755
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +1040 -0
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +347 -0
- claude_mpm/hooks/claude_hooks/services/__init__.py +13 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +190 -0
- claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +282 -0
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +42 -454
- claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
- claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
- claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
- claude_mpm/services/agents/memory/agent_memory_manager.py +42 -508
- claude_mpm/services/agents/memory/memory_categorization_service.py +165 -0
- claude_mpm/services/agents/memory/memory_file_service.py +103 -0
- claude_mpm/services/agents/memory/memory_format_service.py +201 -0
- claude_mpm/services/agents/memory/memory_limits_service.py +99 -0
- claude_mpm/services/agents/registry/__init__.py +1 -1
- claude_mpm/services/cli/__init__.py +18 -0
- claude_mpm/services/cli/agent_cleanup_service.py +407 -0
- claude_mpm/services/cli/agent_dependency_service.py +395 -0
- claude_mpm/services/cli/agent_listing_service.py +463 -0
- claude_mpm/services/cli/agent_output_formatter.py +605 -0
- claude_mpm/services/cli/agent_validation_service.py +589 -0
- claude_mpm/services/cli/dashboard_launcher.py +424 -0
- claude_mpm/services/cli/memory_crud_service.py +617 -0
- claude_mpm/services/cli/memory_output_formatter.py +604 -0
- claude_mpm/services/cli/session_manager.py +513 -0
- claude_mpm/services/cli/socketio_manager.py +498 -0
- claude_mpm/services/cli/startup_checker.py +370 -0
- claude_mpm/services/core/cache_manager.py +311 -0
- claude_mpm/services/core/memory_manager.py +637 -0
- claude_mpm/services/core/path_resolver.py +498 -0
- claude_mpm/services/core/service_container.py +520 -0
- claude_mpm/services/core/service_interfaces.py +436 -0
- claude_mpm/services/diagnostics/checks/agent_check.py +65 -19
- claude_mpm/services/memory/router.py +116 -10
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/METADATA +1 -1
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/RECORD +86 -55
- claude_mpm/cli/commands/run_config_checker.py +0 -159
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/top_level.txt +0 -0
|
@@ -27,7 +27,6 @@ ROLLBACK PROCEDURES:
|
|
|
27
27
|
- Version tracking allows targeted rollbacks
|
|
28
28
|
"""
|
|
29
29
|
|
|
30
|
-
import os
|
|
31
30
|
import time
|
|
32
31
|
from pathlib import Path
|
|
33
32
|
from typing import Any, Dict, List, Optional, Tuple
|
|
@@ -48,7 +47,10 @@ from .agent_metrics_collector import AgentMetricsCollector
|
|
|
48
47
|
from .agent_template_builder import AgentTemplateBuilder
|
|
49
48
|
from .agent_validator import AgentValidator
|
|
50
49
|
from .agent_version_manager import AgentVersionManager
|
|
50
|
+
from .base_agent_locator import BaseAgentLocator
|
|
51
|
+
from .deployment_results_manager import DeploymentResultsManager
|
|
51
52
|
from .multi_source_deployment_service import MultiSourceAgentDeploymentService
|
|
53
|
+
from .single_agent_deployer import SingleAgentDeployer
|
|
52
54
|
|
|
53
55
|
|
|
54
56
|
class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
@@ -121,20 +123,23 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
121
123
|
# Initialize validator service
|
|
122
124
|
self.validator = AgentValidator()
|
|
123
125
|
|
|
126
|
+
# Initialize base agent locator service
|
|
127
|
+
self.base_agent_locator = BaseAgentLocator(self.logger)
|
|
128
|
+
|
|
129
|
+
# Initialize deployment results manager
|
|
130
|
+
self.results_manager = DeploymentResultsManager(self.logger)
|
|
131
|
+
|
|
132
|
+
# Initialize single agent deployer
|
|
133
|
+
self.single_agent_deployer = SingleAgentDeployer(
|
|
134
|
+
self.template_builder,
|
|
135
|
+
self.version_manager,
|
|
136
|
+
self.results_manager,
|
|
137
|
+
self.logger,
|
|
138
|
+
)
|
|
139
|
+
|
|
124
140
|
# Initialize filesystem manager service
|
|
125
141
|
self.filesystem_manager = AgentFileSystemManager()
|
|
126
142
|
|
|
127
|
-
# Initialize deployment metrics tracking
|
|
128
|
-
self._deployment_metrics = {
|
|
129
|
-
"total_deployments": 0,
|
|
130
|
-
"successful_deployments": 0,
|
|
131
|
-
"failed_deployments": 0,
|
|
132
|
-
"migrations_performed": 0,
|
|
133
|
-
"version_migration_count": 0,
|
|
134
|
-
"agent_type_counts": {},
|
|
135
|
-
"deployment_errors": {},
|
|
136
|
-
}
|
|
137
|
-
|
|
138
143
|
# Determine the actual working directory using configuration
|
|
139
144
|
# Priority: param > config > environment > current directory
|
|
140
145
|
self.working_directory = self.get_config_value(
|
|
@@ -169,7 +174,9 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
169
174
|
self.base_agent_path = configured_base_agent
|
|
170
175
|
else:
|
|
171
176
|
# Priority-based search for base_agent.json
|
|
172
|
-
self.base_agent_path = self.
|
|
177
|
+
self.base_agent_path = self.base_agent_locator.find_base_agent_file(
|
|
178
|
+
paths.agents_dir
|
|
179
|
+
)
|
|
173
180
|
|
|
174
181
|
# Initialize configuration manager (after base_agent_path is set)
|
|
175
182
|
self.configuration_manager = AgentConfigurationManager(self.base_agent_path)
|
|
@@ -180,87 +187,6 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
180
187
|
self.logger.info(f"Templates directory: {self.templates_dir}")
|
|
181
188
|
self.logger.info(f"Base agent path: {self.base_agent_path}")
|
|
182
189
|
|
|
183
|
-
def _find_base_agent_file(self) -> Path:
|
|
184
|
-
"""Find base agent file with priority-based search.
|
|
185
|
-
|
|
186
|
-
Priority order:
|
|
187
|
-
1. Environment variable override (CLAUDE_MPM_BASE_AGENT_PATH)
|
|
188
|
-
2. Current working directory (for local development)
|
|
189
|
-
3. Known development locations
|
|
190
|
-
4. User override location (~/.claude/agents/)
|
|
191
|
-
5. Framework agents directory (from paths)
|
|
192
|
-
"""
|
|
193
|
-
# Priority 0: Check environment variable override
|
|
194
|
-
env_path = os.environ.get("CLAUDE_MPM_BASE_AGENT_PATH")
|
|
195
|
-
if env_path:
|
|
196
|
-
env_base_agent = Path(env_path)
|
|
197
|
-
if env_base_agent.exists():
|
|
198
|
-
self.logger.info(
|
|
199
|
-
f"Using environment variable base_agent: {env_base_agent}"
|
|
200
|
-
)
|
|
201
|
-
return env_base_agent
|
|
202
|
-
self.logger.warning(
|
|
203
|
-
f"CLAUDE_MPM_BASE_AGENT_PATH set but file doesn't exist: {env_base_agent}"
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
# Priority 1: Check current working directory for local development
|
|
207
|
-
cwd = Path.cwd()
|
|
208
|
-
cwd_base_agent = cwd / "src" / "claude_mpm" / "agents" / "base_agent.json"
|
|
209
|
-
if cwd_base_agent.exists():
|
|
210
|
-
self.logger.info(
|
|
211
|
-
f"Using local development base_agent from cwd: {cwd_base_agent}"
|
|
212
|
-
)
|
|
213
|
-
return cwd_base_agent
|
|
214
|
-
|
|
215
|
-
# Priority 2: Check known development locations
|
|
216
|
-
known_dev_paths = [
|
|
217
|
-
Path(
|
|
218
|
-
"/Users/masa/Projects/claude-mpm/src/claude_mpm/agents/base_agent.json"
|
|
219
|
-
),
|
|
220
|
-
Path.home()
|
|
221
|
-
/ "Projects"
|
|
222
|
-
/ "claude-mpm"
|
|
223
|
-
/ "src"
|
|
224
|
-
/ "claude_mpm"
|
|
225
|
-
/ "agents"
|
|
226
|
-
/ "base_agent.json",
|
|
227
|
-
Path.home()
|
|
228
|
-
/ "projects"
|
|
229
|
-
/ "claude-mpm"
|
|
230
|
-
/ "src"
|
|
231
|
-
/ "claude_mpm"
|
|
232
|
-
/ "agents"
|
|
233
|
-
/ "base_agent.json",
|
|
234
|
-
]
|
|
235
|
-
|
|
236
|
-
for dev_path in known_dev_paths:
|
|
237
|
-
if dev_path.exists():
|
|
238
|
-
self.logger.info(f"Using development base_agent: {dev_path}")
|
|
239
|
-
return dev_path
|
|
240
|
-
|
|
241
|
-
# Priority 3: Check user override location
|
|
242
|
-
user_base_agent = Path.home() / ".claude" / "agents" / "base_agent.json"
|
|
243
|
-
if user_base_agent.exists():
|
|
244
|
-
self.logger.info(f"Using user override base_agent: {user_base_agent}")
|
|
245
|
-
return user_base_agent
|
|
246
|
-
|
|
247
|
-
# Priority 4: Use framework agents directory (fallback)
|
|
248
|
-
framework_base_agent = paths.agents_dir / "base_agent.json"
|
|
249
|
-
if framework_base_agent.exists():
|
|
250
|
-
self.logger.info(f"Using framework base_agent: {framework_base_agent}")
|
|
251
|
-
return framework_base_agent
|
|
252
|
-
|
|
253
|
-
# If still not found, log all searched locations and raise error
|
|
254
|
-
self.logger.error("Base agent file not found in any location:")
|
|
255
|
-
self.logger.error(f" 1. CWD: {cwd_base_agent}")
|
|
256
|
-
self.logger.error(f" 2. Dev paths: {known_dev_paths}")
|
|
257
|
-
self.logger.error(f" 3. User: {user_base_agent}")
|
|
258
|
-
self.logger.error(f" 4. Framework: {framework_base_agent}")
|
|
259
|
-
|
|
260
|
-
# Final fallback to framework path even if it doesn't exist
|
|
261
|
-
# (will fail later with better error message)
|
|
262
|
-
return framework_base_agent
|
|
263
|
-
|
|
264
190
|
def deploy_agents(
|
|
265
191
|
self,
|
|
266
192
|
target_dir: Optional[Path] = None,
|
|
@@ -362,7 +288,9 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
362
288
|
agents_dir = self._determine_agents_directory(target_dir)
|
|
363
289
|
|
|
364
290
|
# Initialize results dictionary
|
|
365
|
-
results = self.
|
|
291
|
+
results = self.results_manager.initialize_deployment_results(
|
|
292
|
+
agents_dir, deployment_start_time
|
|
293
|
+
)
|
|
366
294
|
|
|
367
295
|
try:
|
|
368
296
|
# Create agents directory if needed
|
|
@@ -372,7 +300,9 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
372
300
|
self._repair_existing_agents(agents_dir, results)
|
|
373
301
|
|
|
374
302
|
# Log deployment source tier
|
|
375
|
-
source_tier = self.
|
|
303
|
+
source_tier = self.base_agent_locator.determine_source_tier(
|
|
304
|
+
self.templates_dir
|
|
305
|
+
)
|
|
376
306
|
self.logger.info(
|
|
377
307
|
f"Building and deploying {source_tier} agents to: {agents_dir}"
|
|
378
308
|
)
|
|
@@ -439,7 +369,7 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
439
369
|
else "single"
|
|
440
370
|
)
|
|
441
371
|
|
|
442
|
-
self.
|
|
372
|
+
self.single_agent_deployer.deploy_single_agent(
|
|
443
373
|
template_file=template_file_path,
|
|
444
374
|
agents_dir=agents_dir,
|
|
445
375
|
base_agent_data=base_agent_data,
|
|
@@ -478,31 +408,24 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
478
408
|
results["errors"].append(error_msg)
|
|
479
409
|
|
|
480
410
|
# METRICS: Track deployment failure
|
|
481
|
-
self.
|
|
482
|
-
error_type = type(e).__name__
|
|
483
|
-
self._deployment_metrics["deployment_errors"][error_type] = (
|
|
484
|
-
self._deployment_metrics["deployment_errors"].get(error_type, 0) + 1
|
|
485
|
-
)
|
|
411
|
+
self.results_manager.update_deployment_metrics(False, type(e).__name__)
|
|
486
412
|
|
|
487
413
|
# METRICS: Calculate final deployment metrics
|
|
488
|
-
|
|
489
|
-
deployment_duration = (deployment_end_time - deployment_start_time) * 1000 # ms
|
|
490
|
-
|
|
491
|
-
results["metrics"]["end_time"] = deployment_end_time
|
|
492
|
-
results["metrics"]["duration_ms"] = deployment_duration
|
|
414
|
+
self.results_manager.finalize_results(results, deployment_start_time)
|
|
493
415
|
|
|
494
416
|
# METRICS: Update rolling averages and statistics
|
|
417
|
+
deployment_duration = results["metrics"].get("duration_ms", 0)
|
|
495
418
|
self.metrics_collector.update_deployment_metrics(deployment_duration, results)
|
|
496
419
|
|
|
497
420
|
return results
|
|
498
421
|
|
|
499
422
|
def get_deployment_metrics(self) -> Dict[str, Any]:
|
|
500
423
|
"""Get current deployment metrics."""
|
|
501
|
-
return self.
|
|
424
|
+
return self.results_manager.get_deployment_metrics()
|
|
502
425
|
|
|
503
426
|
def reset_metrics(self) -> None:
|
|
504
427
|
"""Reset deployment metrics."""
|
|
505
|
-
return self.
|
|
428
|
+
return self.results_manager.reset_metrics()
|
|
506
429
|
|
|
507
430
|
def set_claude_environment(
|
|
508
431
|
self, config_dir: Optional[Path] = None
|
|
@@ -543,74 +466,15 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
543
466
|
- Properly loads base_agent_data before building agent content
|
|
544
467
|
"""
|
|
545
468
|
try:
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
# target_dir should already be the agents directory
|
|
554
|
-
target_dir.mkdir(parents=True, exist_ok=True)
|
|
555
|
-
|
|
556
|
-
# Build and deploy the agent
|
|
557
|
-
target_file = target_dir / f"{agent_name}.md"
|
|
558
|
-
|
|
559
|
-
# Check if update is needed
|
|
560
|
-
if not force_rebuild and target_file.exists():
|
|
561
|
-
# Load base agent data for version checking
|
|
562
|
-
base_agent_data = {}
|
|
563
|
-
base_agent_version = (0, 0, 0)
|
|
564
|
-
if self.base_agent_path.exists():
|
|
565
|
-
try:
|
|
566
|
-
import json
|
|
567
|
-
|
|
568
|
-
base_agent_data = json.loads(self.base_agent_path.read_text())
|
|
569
|
-
base_agent_version = self.version_manager.parse_version(
|
|
570
|
-
base_agent_data.get("base_version")
|
|
571
|
-
or base_agent_data.get("version", 0)
|
|
572
|
-
)
|
|
573
|
-
except Exception as e:
|
|
574
|
-
self.logger.warning(
|
|
575
|
-
f"Could not load base agent for version check: {e}"
|
|
576
|
-
)
|
|
577
|
-
|
|
578
|
-
needs_update, reason = self.version_manager.check_agent_needs_update(
|
|
579
|
-
target_file, template_file, base_agent_version
|
|
580
|
-
)
|
|
581
|
-
if not needs_update:
|
|
582
|
-
self.logger.info(f"Agent {agent_name} is up to date")
|
|
583
|
-
return True
|
|
584
|
-
self.logger.info(f"Updating agent {agent_name}: {reason}")
|
|
585
|
-
|
|
586
|
-
# Load base agent data for building
|
|
587
|
-
base_agent_data = {}
|
|
588
|
-
if self.base_agent_path.exists():
|
|
589
|
-
try:
|
|
590
|
-
import json
|
|
591
|
-
|
|
592
|
-
base_agent_data = json.loads(self.base_agent_path.read_text())
|
|
593
|
-
except Exception as e:
|
|
594
|
-
self.logger.warning(f"Could not load base agent: {e}")
|
|
595
|
-
|
|
596
|
-
# Build the agent markdown
|
|
597
|
-
# For single agent deployment, determine source from template location
|
|
598
|
-
source_info = self._determine_agent_source(template_file)
|
|
599
|
-
agent_content = self.template_builder.build_agent_markdown(
|
|
600
|
-
agent_name, template_file, base_agent_data, source_info
|
|
601
|
-
)
|
|
602
|
-
if not agent_content:
|
|
603
|
-
self.logger.error(f"Failed to build agent content for {agent_name}")
|
|
604
|
-
return False
|
|
605
|
-
|
|
606
|
-
# Write to target file
|
|
607
|
-
target_file.write_text(agent_content)
|
|
608
|
-
self.logger.info(
|
|
609
|
-
f"Successfully deployed agent: {agent_name} to {target_file}"
|
|
469
|
+
return self.single_agent_deployer.deploy_agent(
|
|
470
|
+
agent_name,
|
|
471
|
+
self.templates_dir,
|
|
472
|
+
target_dir,
|
|
473
|
+
self.base_agent_path,
|
|
474
|
+
force_rebuild,
|
|
475
|
+
self.working_directory,
|
|
610
476
|
)
|
|
611
477
|
|
|
612
|
-
return True
|
|
613
|
-
|
|
614
478
|
except AgentDeploymentError:
|
|
615
479
|
# Re-raise our custom exceptions
|
|
616
480
|
raise
|
|
@@ -764,11 +628,9 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
764
628
|
self.logger.info(f"Async deployment completed in {duration_ms:.1f}ms")
|
|
765
629
|
|
|
766
630
|
# Update internal metrics
|
|
767
|
-
self.
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
else:
|
|
771
|
-
self._deployment_metrics["failed_deployments"] += 1
|
|
631
|
+
self.results_manager.update_deployment_metrics(
|
|
632
|
+
not results.get("errors")
|
|
633
|
+
)
|
|
772
634
|
|
|
773
635
|
return results
|
|
774
636
|
|
|
@@ -793,43 +655,6 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
793
655
|
resolver = AgentsDirectoryResolver(self.working_directory)
|
|
794
656
|
return resolver.determine_agents_directory(target_dir)
|
|
795
657
|
|
|
796
|
-
def _initialize_deployment_results(
|
|
797
|
-
self, agents_dir: Path, deployment_start_time: float
|
|
798
|
-
) -> Dict[str, Any]:
|
|
799
|
-
"""
|
|
800
|
-
Initialize the deployment results dictionary.
|
|
801
|
-
|
|
802
|
-
WHY: Consistent result structure ensures all deployment
|
|
803
|
-
operations return the same format for easier processing.
|
|
804
|
-
|
|
805
|
-
Args:
|
|
806
|
-
agents_dir: Target agents directory
|
|
807
|
-
deployment_start_time: Start time for metrics
|
|
808
|
-
|
|
809
|
-
Returns:
|
|
810
|
-
Initialized results dictionary
|
|
811
|
-
"""
|
|
812
|
-
return {
|
|
813
|
-
"target_dir": str(agents_dir),
|
|
814
|
-
"deployed": [],
|
|
815
|
-
"errors": [],
|
|
816
|
-
"skipped": [],
|
|
817
|
-
"updated": [],
|
|
818
|
-
"migrated": [], # Track agents migrated from old format
|
|
819
|
-
"converted": [], # Track YAML to MD conversions
|
|
820
|
-
"repaired": [], # Track agents with repaired frontmatter
|
|
821
|
-
"total": 0,
|
|
822
|
-
# METRICS: Add detailed timing and performance data to results
|
|
823
|
-
"metrics": {
|
|
824
|
-
"start_time": deployment_start_time,
|
|
825
|
-
"end_time": None,
|
|
826
|
-
"duration_ms": None,
|
|
827
|
-
"agent_timings": {}, # Track individual agent deployment times
|
|
828
|
-
"validation_times": {}, # Track template validation times
|
|
829
|
-
"resource_usage": {}, # Could track memory/CPU if needed
|
|
830
|
-
},
|
|
831
|
-
}
|
|
832
|
-
|
|
833
658
|
def _repair_existing_agents(
|
|
834
659
|
self, agents_dir: Path, results: Dict[str, Any]
|
|
835
660
|
) -> None:
|
|
@@ -852,12 +677,6 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
852
677
|
for agent_name in repair_results["repaired"]:
|
|
853
678
|
self.logger.debug(f" - Repaired: {agent_name}")
|
|
854
679
|
|
|
855
|
-
def _determine_source_tier(self) -> str:
|
|
856
|
-
"""Determine the source tier for logging."""
|
|
857
|
-
from .deployment_type_detector import DeploymentTypeDetector
|
|
858
|
-
|
|
859
|
-
return DeploymentTypeDetector.determine_source_tier(self.templates_dir)
|
|
860
|
-
|
|
861
680
|
def _load_base_agent(self) -> tuple:
|
|
862
681
|
"""Load base agent content and version."""
|
|
863
682
|
return self.configuration_manager.load_base_agent()
|
|
@@ -866,205 +685,6 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
866
685
|
"""Get and filter template files based on exclusion rules."""
|
|
867
686
|
return self.discovery_service.get_filtered_templates(excluded_agents, config)
|
|
868
687
|
|
|
869
|
-
def _deploy_single_agent(
|
|
870
|
-
self,
|
|
871
|
-
template_file: Path,
|
|
872
|
-
agents_dir: Path,
|
|
873
|
-
base_agent_data: dict,
|
|
874
|
-
base_agent_version: tuple,
|
|
875
|
-
force_rebuild: bool,
|
|
876
|
-
deployment_mode: str,
|
|
877
|
-
results: Dict[str, Any],
|
|
878
|
-
source_info: str = "unknown",
|
|
879
|
-
) -> None:
|
|
880
|
-
"""
|
|
881
|
-
Deploy a single agent template.
|
|
882
|
-
|
|
883
|
-
WHY: Extracting single agent deployment logic reduces complexity
|
|
884
|
-
and makes the main deployment loop more readable.
|
|
885
|
-
|
|
886
|
-
Args:
|
|
887
|
-
template_file: Agent template file
|
|
888
|
-
agents_dir: Target agents directory
|
|
889
|
-
base_agent_data: Base agent data
|
|
890
|
-
base_agent_version: Base agent version
|
|
891
|
-
force_rebuild: Whether to force rebuild
|
|
892
|
-
deployment_mode: Deployment mode (update/project)
|
|
893
|
-
results: Results dictionary to update
|
|
894
|
-
source_info: Source of the agent (system/project/user)
|
|
895
|
-
"""
|
|
896
|
-
try:
|
|
897
|
-
# METRICS: Track individual agent deployment time
|
|
898
|
-
agent_start_time = time.time()
|
|
899
|
-
|
|
900
|
-
agent_name = template_file.stem
|
|
901
|
-
target_file = agents_dir / f"{agent_name}.md"
|
|
902
|
-
|
|
903
|
-
# Check if agent needs update
|
|
904
|
-
needs_update, is_migration, reason = self._check_update_status(
|
|
905
|
-
target_file,
|
|
906
|
-
template_file,
|
|
907
|
-
base_agent_version,
|
|
908
|
-
force_rebuild,
|
|
909
|
-
deployment_mode,
|
|
910
|
-
)
|
|
911
|
-
|
|
912
|
-
# Skip if exists and doesn't need update (only in update mode)
|
|
913
|
-
if (
|
|
914
|
-
target_file.exists()
|
|
915
|
-
and not needs_update
|
|
916
|
-
and deployment_mode != "project"
|
|
917
|
-
):
|
|
918
|
-
results["skipped"].append(agent_name)
|
|
919
|
-
self.logger.debug(f"Skipped up-to-date agent: {agent_name}")
|
|
920
|
-
return
|
|
921
|
-
|
|
922
|
-
# Build the agent file as markdown with YAML frontmatter
|
|
923
|
-
agent_content = self.template_builder.build_agent_markdown(
|
|
924
|
-
agent_name, template_file, base_agent_data, source_info
|
|
925
|
-
)
|
|
926
|
-
|
|
927
|
-
# Write the agent file
|
|
928
|
-
is_update = target_file.exists()
|
|
929
|
-
target_file.write_text(agent_content)
|
|
930
|
-
|
|
931
|
-
# Record metrics and update results
|
|
932
|
-
self._record_agent_deployment(
|
|
933
|
-
agent_name,
|
|
934
|
-
template_file,
|
|
935
|
-
target_file,
|
|
936
|
-
is_update,
|
|
937
|
-
is_migration,
|
|
938
|
-
reason,
|
|
939
|
-
agent_start_time,
|
|
940
|
-
results,
|
|
941
|
-
)
|
|
942
|
-
|
|
943
|
-
except AgentDeploymentError as e:
|
|
944
|
-
# Re-raise our custom exceptions
|
|
945
|
-
self.logger.error(str(e))
|
|
946
|
-
results["errors"].append(str(e))
|
|
947
|
-
except Exception as e:
|
|
948
|
-
# Wrap generic exceptions with context
|
|
949
|
-
error_msg = f"Failed to build {template_file.name}: {e}"
|
|
950
|
-
self.logger.error(error_msg)
|
|
951
|
-
results["errors"].append(error_msg)
|
|
952
|
-
|
|
953
|
-
def _check_update_status(
|
|
954
|
-
self,
|
|
955
|
-
target_file: Path,
|
|
956
|
-
template_file: Path,
|
|
957
|
-
base_agent_version: tuple,
|
|
958
|
-
force_rebuild: bool,
|
|
959
|
-
deployment_mode: str,
|
|
960
|
-
) -> tuple:
|
|
961
|
-
"""
|
|
962
|
-
Check if agent needs update and determine status.
|
|
963
|
-
|
|
964
|
-
WHY: Centralized update checking logic ensures consistent
|
|
965
|
-
version comparison and migration detection.
|
|
966
|
-
|
|
967
|
-
Args:
|
|
968
|
-
target_file: Target agent file
|
|
969
|
-
template_file: Template file
|
|
970
|
-
base_agent_version: Base agent version
|
|
971
|
-
force_rebuild: Whether to force rebuild
|
|
972
|
-
deployment_mode: Deployment mode
|
|
973
|
-
|
|
974
|
-
Returns:
|
|
975
|
-
Tuple of (needs_update, is_migration, reason)
|
|
976
|
-
"""
|
|
977
|
-
needs_update = force_rebuild
|
|
978
|
-
is_migration = False
|
|
979
|
-
reason = ""
|
|
980
|
-
|
|
981
|
-
# In project deployment mode, always deploy regardless of version
|
|
982
|
-
if deployment_mode == "project":
|
|
983
|
-
if target_file.exists():
|
|
984
|
-
needs_update = True
|
|
985
|
-
self.logger.debug(
|
|
986
|
-
f"Project deployment mode: will deploy {template_file.stem}"
|
|
987
|
-
)
|
|
988
|
-
else:
|
|
989
|
-
needs_update = True
|
|
990
|
-
elif not needs_update and target_file.exists():
|
|
991
|
-
# In update mode, check version compatibility
|
|
992
|
-
needs_update, reason = self.version_manager.check_agent_needs_update(
|
|
993
|
-
target_file, template_file, base_agent_version
|
|
994
|
-
)
|
|
995
|
-
if needs_update:
|
|
996
|
-
# Check if this is a migration from old format
|
|
997
|
-
if "migration needed" in reason:
|
|
998
|
-
is_migration = True
|
|
999
|
-
self.logger.info(f"Migrating agent {template_file.stem}: {reason}")
|
|
1000
|
-
else:
|
|
1001
|
-
self.logger.info(
|
|
1002
|
-
f"Agent {template_file.stem} needs update: {reason}"
|
|
1003
|
-
)
|
|
1004
|
-
|
|
1005
|
-
return needs_update, is_migration, reason
|
|
1006
|
-
|
|
1007
|
-
def _record_agent_deployment(
|
|
1008
|
-
self,
|
|
1009
|
-
agent_name: str,
|
|
1010
|
-
template_file: Path,
|
|
1011
|
-
target_file: Path,
|
|
1012
|
-
is_update: bool,
|
|
1013
|
-
is_migration: bool,
|
|
1014
|
-
reason: str,
|
|
1015
|
-
agent_start_time: float,
|
|
1016
|
-
results: Dict[str, Any],
|
|
1017
|
-
) -> None:
|
|
1018
|
-
"""
|
|
1019
|
-
Record deployment metrics and update results.
|
|
1020
|
-
|
|
1021
|
-
WHY: Centralized metrics recording ensures consistent tracking
|
|
1022
|
-
of deployment performance and statistics.
|
|
1023
|
-
|
|
1024
|
-
Args:
|
|
1025
|
-
agent_name: Name of the agent
|
|
1026
|
-
template_file: Template file
|
|
1027
|
-
target_file: Target file
|
|
1028
|
-
is_update: Whether this is an update
|
|
1029
|
-
is_migration: Whether this is a migration
|
|
1030
|
-
reason: Update/migration reason
|
|
1031
|
-
agent_start_time: Start time for this agent
|
|
1032
|
-
results: Results dictionary to update
|
|
1033
|
-
"""
|
|
1034
|
-
# METRICS: Record deployment time for this agent
|
|
1035
|
-
agent_deployment_time = (time.time() - agent_start_time) * 1000 # Convert to ms
|
|
1036
|
-
results["metrics"]["agent_timings"][agent_name] = agent_deployment_time
|
|
1037
|
-
|
|
1038
|
-
# METRICS: Update agent type deployment counts
|
|
1039
|
-
self._deployment_metrics["agent_type_counts"][agent_name] = (
|
|
1040
|
-
self._deployment_metrics["agent_type_counts"].get(agent_name, 0) + 1
|
|
1041
|
-
)
|
|
1042
|
-
|
|
1043
|
-
deployment_info = {
|
|
1044
|
-
"name": agent_name,
|
|
1045
|
-
"template": str(template_file),
|
|
1046
|
-
"target": str(target_file),
|
|
1047
|
-
"deployment_time_ms": agent_deployment_time,
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
if is_migration:
|
|
1051
|
-
deployment_info["reason"] = reason
|
|
1052
|
-
results["migrated"].append(deployment_info)
|
|
1053
|
-
self.logger.info(
|
|
1054
|
-
f"Successfully migrated agent: {agent_name} to semantic versioning"
|
|
1055
|
-
)
|
|
1056
|
-
|
|
1057
|
-
# METRICS: Track migration statistics
|
|
1058
|
-
self._deployment_metrics["migrations_performed"] += 1
|
|
1059
|
-
self._deployment_metrics["version_migration_count"] += 1
|
|
1060
|
-
|
|
1061
|
-
elif is_update:
|
|
1062
|
-
results["updated"].append(deployment_info)
|
|
1063
|
-
self.logger.debug(f"Updated agent: {agent_name}")
|
|
1064
|
-
else:
|
|
1065
|
-
results["deployed"].append(deployment_info)
|
|
1066
|
-
self.logger.debug(f"Built and deployed agent: {agent_name}")
|
|
1067
|
-
|
|
1068
688
|
def _validate_and_repair_existing_agents(self, agents_dir: Path) -> Dict[str, Any]:
|
|
1069
689
|
"""Validate and repair broken frontmatter in existing agent files."""
|
|
1070
690
|
from .agent_frontmatter_validator import AgentFrontmatterValidator
|
|
@@ -1072,38 +692,6 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
1072
692
|
validator = AgentFrontmatterValidator(self.logger)
|
|
1073
693
|
return validator.validate_and_repair_existing_agents(agents_dir)
|
|
1074
694
|
|
|
1075
|
-
def _determine_agent_source(self, template_path: Path) -> str:
|
|
1076
|
-
"""Determine the source of an agent from its template path.
|
|
1077
|
-
|
|
1078
|
-
WHY: When deploying single agents, we need to track their source
|
|
1079
|
-
for proper version management and debugging.
|
|
1080
|
-
|
|
1081
|
-
Args:
|
|
1082
|
-
template_path: Path to the agent template
|
|
1083
|
-
|
|
1084
|
-
Returns:
|
|
1085
|
-
Source string (system/project/user/unknown)
|
|
1086
|
-
"""
|
|
1087
|
-
template_str = str(template_path.resolve())
|
|
1088
|
-
|
|
1089
|
-
# Check if it's a system template
|
|
1090
|
-
if (
|
|
1091
|
-
"/claude_mpm/agents/templates/" in template_str
|
|
1092
|
-
or "/src/claude_mpm/agents/templates/" in template_str
|
|
1093
|
-
):
|
|
1094
|
-
return "system"
|
|
1095
|
-
|
|
1096
|
-
# Check if it's a project agent
|
|
1097
|
-
if "/.claude-mpm/agents/" in template_str:
|
|
1098
|
-
# Check if it's in the current working directory
|
|
1099
|
-
if str(self.working_directory) in template_str:
|
|
1100
|
-
return "project"
|
|
1101
|
-
# Check if it's in user home
|
|
1102
|
-
if str(Path.home()) in template_str:
|
|
1103
|
-
return "user"
|
|
1104
|
-
|
|
1105
|
-
return "unknown"
|
|
1106
|
-
|
|
1107
695
|
def _should_use_multi_source_deployment(self, deployment_mode: str) -> bool:
|
|
1108
696
|
"""Determine if multi-source deployment should be used.
|
|
1109
697
|
|