claude-mpm 2.1.1__py3-none-any.whl → 3.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/_version.py +2 -2
- claude_mpm/agents/agent_loader.py +682 -102
- claude_mpm/agents/base_agent_loader.py +23 -8
- claude_mpm/agents/schema/agent_schema.json +237 -83
- claude_mpm/agents/templates/data_engineer.json +6 -3
- claude_mpm/agents/templates/documentation.json +6 -3
- claude_mpm/agents/templates/engineer.json +7 -4
- claude_mpm/agents/templates/ops.json +6 -3
- claude_mpm/agents/templates/qa.json +10 -5
- claude_mpm/agents/templates/research.json +31 -42
- claude_mpm/agents/templates/security.json +14 -6
- claude_mpm/agents/templates/version_control.json +9 -5
- claude_mpm/core/base_service.py +61 -1
- claude_mpm/hooks/claude_hooks/hook_handler.py +224 -20
- claude_mpm/schemas/README_SECURITY.md +92 -0
- claude_mpm/schemas/agent_schema.json +130 -51
- claude_mpm/schemas/agent_schema_security_notes.md +165 -0
- claude_mpm/services/agent_capabilities_generator.py +0 -1
- claude_mpm/services/agent_deployment.py +479 -91
- claude_mpm/services/agent_lifecycle_manager.py +62 -4
- claude_mpm/services/deployed_agent_discovery.py +0 -1
- claude_mpm/services/version_control/semantic_versioning.py +165 -16
- claude_mpm/validation/agent_validator.py +147 -13
- {claude_mpm-2.1.1.dist-info → claude_mpm-3.0.1.dist-info}/METADATA +2 -2
- {claude_mpm-2.1.1.dist-info → claude_mpm-3.0.1.dist-info}/RECORD +29 -29
- claude_mpm/cli_old/__init__.py +0 -1
- claude_mpm/cli_old/ticket_cli.py +0 -102
- {claude_mpm-2.1.1.dist-info → claude_mpm-3.0.1.dist-info}/WHEEL +0 -0
- {claude_mpm-2.1.1.dist-info → claude_mpm-3.0.1.dist-info}/entry_points.txt +0 -0
- {claude_mpm-2.1.1.dist-info → claude_mpm-3.0.1.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-2.1.1.dist-info → claude_mpm-3.0.1.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,36 @@
|
|
|
1
|
-
"""Agent deployment service for Claude Code native subagents.
|
|
1
|
+
"""Agent deployment service for Claude Code native subagents.
|
|
2
|
+
|
|
3
|
+
This service handles the complete lifecycle of agent deployment:
|
|
4
|
+
1. Building agent YAML files from JSON templates
|
|
5
|
+
2. Managing versioning and updates
|
|
6
|
+
3. Deploying to Claude Code's .claude/agents directory
|
|
7
|
+
4. Environment configuration for agent discovery
|
|
8
|
+
5. Deployment verification and cleanup
|
|
9
|
+
|
|
10
|
+
OPERATIONAL CONSIDERATIONS:
|
|
11
|
+
- Deployment is idempotent - safe to run multiple times
|
|
12
|
+
- Version checking prevents unnecessary rebuilds (saves I/O)
|
|
13
|
+
- Supports force rebuild for troubleshooting
|
|
14
|
+
- Maintains backward compatibility with legacy versions
|
|
15
|
+
- Handles migration from old serial versioning to semantic versioning
|
|
16
|
+
|
|
17
|
+
MONITORING:
|
|
18
|
+
- Check logs for deployment status and errors
|
|
19
|
+
- Monitor disk space in .claude/agents directory
|
|
20
|
+
- Track version migration progress
|
|
21
|
+
- Verify agent discovery after deployment
|
|
22
|
+
|
|
23
|
+
ROLLBACK PROCEDURES:
|
|
24
|
+
- Keep backups of .claude/agents before major updates
|
|
25
|
+
- Use clean_deployment() to remove system agents
|
|
26
|
+
- User-created agents are preserved during cleanup
|
|
27
|
+
- Version tracking allows targeted rollbacks
|
|
28
|
+
"""
|
|
2
29
|
|
|
3
30
|
import os
|
|
4
31
|
import shutil
|
|
5
32
|
import logging
|
|
33
|
+
import time
|
|
6
34
|
from pathlib import Path
|
|
7
35
|
from typing import Optional, List, Dict, Any
|
|
8
36
|
|
|
@@ -11,7 +39,35 @@ from claude_mpm.constants import EnvironmentVars, Paths, AgentMetadata
|
|
|
11
39
|
|
|
12
40
|
|
|
13
41
|
class AgentDeploymentService:
|
|
14
|
-
"""Service for deploying Claude Code native agents.
|
|
42
|
+
"""Service for deploying Claude Code native agents.
|
|
43
|
+
|
|
44
|
+
METRICS COLLECTION OPPORTUNITIES:
|
|
45
|
+
This service could collect valuable deployment metrics including:
|
|
46
|
+
- Agent deployment frequency and success rates
|
|
47
|
+
- Template validation performance
|
|
48
|
+
- Version migration patterns
|
|
49
|
+
- Deployment duration by agent type
|
|
50
|
+
- Cache hit rates for agent templates
|
|
51
|
+
- Resource usage during deployment (memory, CPU)
|
|
52
|
+
- Agent file sizes and complexity metrics
|
|
53
|
+
- Deployment failure reasons and patterns
|
|
54
|
+
|
|
55
|
+
DEPLOYMENT PIPELINE:
|
|
56
|
+
1. Initialize with template and base agent paths
|
|
57
|
+
2. Load base agent configuration (shared settings)
|
|
58
|
+
3. Iterate through agent templates
|
|
59
|
+
4. Check version and update requirements
|
|
60
|
+
5. Build YAML files with proper formatting
|
|
61
|
+
6. Deploy to target directory
|
|
62
|
+
7. Set environment variables for discovery
|
|
63
|
+
8. Verify deployment success
|
|
64
|
+
|
|
65
|
+
ENVIRONMENT REQUIREMENTS:
|
|
66
|
+
- Write access to .claude/agents directory
|
|
67
|
+
- Python 3.8+ for pathlib and typing features
|
|
68
|
+
- JSON parsing for template files
|
|
69
|
+
- YAML generation capabilities
|
|
70
|
+
"""
|
|
15
71
|
|
|
16
72
|
def __init__(self, templates_dir: Optional[Path] = None, base_agent_path: Optional[Path] = None):
|
|
17
73
|
"""
|
|
@@ -20,9 +76,29 @@ class AgentDeploymentService:
|
|
|
20
76
|
Args:
|
|
21
77
|
templates_dir: Directory containing agent template files
|
|
22
78
|
base_agent_path: Path to base_agent.md file
|
|
79
|
+
|
|
80
|
+
METRICS OPPORTUNITY: Track initialization performance:
|
|
81
|
+
- Template directory scan time
|
|
82
|
+
- Base agent loading time
|
|
83
|
+
- Initial validation overhead
|
|
23
84
|
"""
|
|
24
85
|
self.logger = get_logger(self.__class__.__name__)
|
|
25
86
|
|
|
87
|
+
# METRICS: Initialize deployment metrics tracking
|
|
88
|
+
# This data structure would be used for collecting deployment telemetry
|
|
89
|
+
self._deployment_metrics = {
|
|
90
|
+
'total_deployments': 0,
|
|
91
|
+
'successful_deployments': 0,
|
|
92
|
+
'failed_deployments': 0,
|
|
93
|
+
'migrations_performed': 0,
|
|
94
|
+
'average_deployment_time_ms': 0.0,
|
|
95
|
+
'deployment_times': [], # Keep last 100 for rolling average
|
|
96
|
+
'agent_type_counts': {}, # Track deployments by agent type
|
|
97
|
+
'version_migration_count': 0,
|
|
98
|
+
'template_validation_times': {}, # Track validation performance
|
|
99
|
+
'deployment_errors': {} # Track error types and frequencies
|
|
100
|
+
}
|
|
101
|
+
|
|
26
102
|
# Find templates directory
|
|
27
103
|
module_path = Path(__file__).parent.parent
|
|
28
104
|
if templates_dir:
|
|
@@ -46,13 +122,59 @@ class AgentDeploymentService:
|
|
|
46
122
|
Build and deploy agents by combining base_agent.md with templates.
|
|
47
123
|
Also deploys system instructions for PM framework.
|
|
48
124
|
|
|
125
|
+
METRICS COLLECTED:
|
|
126
|
+
- Deployment start/end timestamps
|
|
127
|
+
- Individual agent deployment durations
|
|
128
|
+
- Success/failure rates by agent type
|
|
129
|
+
- Version migration statistics
|
|
130
|
+
- Template validation performance
|
|
131
|
+
- Error type frequencies
|
|
132
|
+
|
|
133
|
+
OPERATIONAL FLOW:
|
|
134
|
+
1. Validates target directory (creates if needed)
|
|
135
|
+
2. Loads base agent configuration
|
|
136
|
+
3. Discovers all agent templates
|
|
137
|
+
4. For each agent:
|
|
138
|
+
- Checks if update needed (version comparison)
|
|
139
|
+
- Builds YAML configuration
|
|
140
|
+
- Writes to target directory
|
|
141
|
+
- Tracks deployment status
|
|
142
|
+
|
|
143
|
+
PERFORMANCE CONSIDERATIONS:
|
|
144
|
+
- Skips unchanged agents (version-based caching)
|
|
145
|
+
- Batch processes all agents in single pass
|
|
146
|
+
- Minimal file I/O with in-memory building
|
|
147
|
+
- Parallel-safe (no shared state mutations)
|
|
148
|
+
|
|
149
|
+
ERROR HANDLING:
|
|
150
|
+
- Continues deployment on individual agent failures
|
|
151
|
+
- Collects all errors for reporting
|
|
152
|
+
- Logs detailed error context
|
|
153
|
+
- Returns comprehensive results dict
|
|
154
|
+
|
|
155
|
+
MONITORING POINTS:
|
|
156
|
+
- Track total deployment time
|
|
157
|
+
- Monitor skipped vs updated vs new agents
|
|
158
|
+
- Check error rates and patterns
|
|
159
|
+
- Verify migration completion
|
|
160
|
+
|
|
49
161
|
Args:
|
|
50
162
|
target_dir: Target directory for agents (default: .claude/agents/)
|
|
51
|
-
force_rebuild: Force rebuild even if agents exist
|
|
163
|
+
force_rebuild: Force rebuild even if agents exist (useful for troubleshooting)
|
|
52
164
|
|
|
53
165
|
Returns:
|
|
54
|
-
Dictionary with deployment results
|
|
166
|
+
Dictionary with deployment results:
|
|
167
|
+
- target_dir: Deployment location
|
|
168
|
+
- deployed: List of newly deployed agents
|
|
169
|
+
- updated: List of updated agents
|
|
170
|
+
- migrated: List of agents migrated to new version format
|
|
171
|
+
- skipped: List of unchanged agents
|
|
172
|
+
- errors: List of deployment errors
|
|
173
|
+
- total: Total number of agents processed
|
|
55
174
|
"""
|
|
175
|
+
# METRICS: Record deployment start time for performance tracking
|
|
176
|
+
deployment_start_time = time.time()
|
|
177
|
+
|
|
56
178
|
if not target_dir:
|
|
57
179
|
target_dir = Path(Paths.CLAUDE_AGENTS_DIR.value).expanduser()
|
|
58
180
|
|
|
@@ -64,7 +186,16 @@ class AgentDeploymentService:
|
|
|
64
186
|
"skipped": [],
|
|
65
187
|
"updated": [],
|
|
66
188
|
"migrated": [], # Track agents migrated from old format
|
|
67
|
-
"total": 0
|
|
189
|
+
"total": 0,
|
|
190
|
+
# METRICS: Add detailed timing and performance data to results
|
|
191
|
+
"metrics": {
|
|
192
|
+
"start_time": deployment_start_time,
|
|
193
|
+
"end_time": None,
|
|
194
|
+
"duration_ms": None,
|
|
195
|
+
"agent_timings": {}, # Track individual agent deployment times
|
|
196
|
+
"validation_times": {}, # Track template validation times
|
|
197
|
+
"resource_usage": {} # Could track memory/CPU if needed
|
|
198
|
+
}
|
|
68
199
|
}
|
|
69
200
|
|
|
70
201
|
try:
|
|
@@ -82,6 +213,9 @@ class AgentDeploymentService:
|
|
|
82
213
|
return results
|
|
83
214
|
|
|
84
215
|
# Load base agent content
|
|
216
|
+
# OPERATIONAL NOTE: Base agent contains shared configuration and instructions
|
|
217
|
+
# that all agents inherit. This reduces duplication and ensures consistency.
|
|
218
|
+
# If base agent fails to load, deployment continues with agent-specific configs only.
|
|
85
219
|
base_agent_data = {}
|
|
86
220
|
base_agent_version = 0
|
|
87
221
|
if self.base_agent_path.exists():
|
|
@@ -89,9 +223,11 @@ class AgentDeploymentService:
|
|
|
89
223
|
import json
|
|
90
224
|
base_agent_data = json.loads(self.base_agent_path.read_text())
|
|
91
225
|
# Handle both 'base_version' (new format) and 'version' (old format)
|
|
226
|
+
# MIGRATION PATH: Supporting both formats during transition period
|
|
92
227
|
base_agent_version = self._parse_version(base_agent_data.get('base_version') or base_agent_data.get('version', 0))
|
|
93
228
|
self.logger.info(f"Loaded base agent template (version {self._format_version_display(base_agent_version)})")
|
|
94
229
|
except Exception as e:
|
|
230
|
+
# NON-FATAL: Base agent is optional enhancement, not required
|
|
95
231
|
self.logger.warning(f"Could not load base agent: {e}")
|
|
96
232
|
|
|
97
233
|
# Get all template files
|
|
@@ -102,8 +238,11 @@ class AgentDeploymentService:
|
|
|
102
238
|
|
|
103
239
|
for template_file in template_files:
|
|
104
240
|
try:
|
|
241
|
+
# METRICS: Track individual agent deployment time
|
|
242
|
+
agent_start_time = time.time()
|
|
243
|
+
|
|
105
244
|
agent_name = template_file.stem
|
|
106
|
-
target_file = target_dir / f"{agent_name}.
|
|
245
|
+
target_file = target_dir / f"{agent_name}.yaml"
|
|
107
246
|
|
|
108
247
|
# Check if agent needs update
|
|
109
248
|
needs_update = force_rebuild
|
|
@@ -127,32 +266,48 @@ class AgentDeploymentService:
|
|
|
127
266
|
continue
|
|
128
267
|
|
|
129
268
|
# Build the agent file
|
|
130
|
-
|
|
269
|
+
agent_yaml = self._build_agent_yaml(agent_name, template_file, base_agent_data)
|
|
131
270
|
|
|
132
271
|
# Write the agent file
|
|
133
272
|
is_update = target_file.exists()
|
|
134
|
-
target_file.write_text(
|
|
273
|
+
target_file.write_text(agent_yaml)
|
|
274
|
+
|
|
275
|
+
# METRICS: Record deployment time for this agent
|
|
276
|
+
agent_deployment_time = (time.time() - agent_start_time) * 1000 # Convert to ms
|
|
277
|
+
results["metrics"]["agent_timings"][agent_name] = agent_deployment_time
|
|
278
|
+
|
|
279
|
+
# METRICS: Update agent type deployment counts
|
|
280
|
+
self._deployment_metrics['agent_type_counts'][agent_name] = \
|
|
281
|
+
self._deployment_metrics['agent_type_counts'].get(agent_name, 0) + 1
|
|
135
282
|
|
|
136
283
|
if is_migration:
|
|
137
284
|
results["migrated"].append({
|
|
138
285
|
"name": agent_name,
|
|
139
286
|
"template": str(template_file),
|
|
140
287
|
"target": str(target_file),
|
|
141
|
-
"reason": reason
|
|
288
|
+
"reason": reason,
|
|
289
|
+
"deployment_time_ms": agent_deployment_time # METRICS: Include timing
|
|
142
290
|
})
|
|
143
291
|
self.logger.info(f"Successfully migrated agent: {agent_name} to semantic versioning")
|
|
292
|
+
|
|
293
|
+
# METRICS: Track migration statistics
|
|
294
|
+
self._deployment_metrics['migrations_performed'] += 1
|
|
295
|
+
self._deployment_metrics['version_migration_count'] += 1
|
|
296
|
+
|
|
144
297
|
elif is_update:
|
|
145
298
|
results["updated"].append({
|
|
146
299
|
"name": agent_name,
|
|
147
300
|
"template": str(template_file),
|
|
148
|
-
"target": str(target_file)
|
|
301
|
+
"target": str(target_file),
|
|
302
|
+
"deployment_time_ms": agent_deployment_time # METRICS: Include timing
|
|
149
303
|
})
|
|
150
304
|
self.logger.debug(f"Updated agent: {agent_name}")
|
|
151
305
|
else:
|
|
152
306
|
results["deployed"].append({
|
|
153
307
|
"name": agent_name,
|
|
154
308
|
"template": str(template_file),
|
|
155
|
-
"target": str(target_file)
|
|
309
|
+
"target": str(target_file),
|
|
310
|
+
"deployment_time_ms": agent_deployment_time # METRICS: Include timing
|
|
156
311
|
})
|
|
157
312
|
self.logger.debug(f"Built and deployed agent: {agent_name}")
|
|
158
313
|
|
|
@@ -173,9 +328,122 @@ class AgentDeploymentService:
|
|
|
173
328
|
error_msg = f"Agent deployment failed: {e}"
|
|
174
329
|
self.logger.error(error_msg)
|
|
175
330
|
results["errors"].append(error_msg)
|
|
331
|
+
|
|
332
|
+
# METRICS: Track deployment failure
|
|
333
|
+
self._deployment_metrics['failed_deployments'] += 1
|
|
334
|
+
error_type = type(e).__name__
|
|
335
|
+
self._deployment_metrics['deployment_errors'][error_type] = \
|
|
336
|
+
self._deployment_metrics['deployment_errors'].get(error_type, 0) + 1
|
|
337
|
+
|
|
338
|
+
# METRICS: Calculate final deployment metrics
|
|
339
|
+
deployment_end_time = time.time()
|
|
340
|
+
deployment_duration = (deployment_end_time - deployment_start_time) * 1000 # ms
|
|
341
|
+
|
|
342
|
+
results["metrics"]["end_time"] = deployment_end_time
|
|
343
|
+
results["metrics"]["duration_ms"] = deployment_duration
|
|
344
|
+
|
|
345
|
+
# METRICS: Update rolling averages and statistics
|
|
346
|
+
self._update_deployment_metrics(deployment_duration, results)
|
|
176
347
|
|
|
177
348
|
return results
|
|
178
349
|
|
|
350
|
+
def _update_deployment_metrics(self, duration_ms: float, results: Dict[str, Any]) -> None:
|
|
351
|
+
"""
|
|
352
|
+
Update internal deployment metrics.
|
|
353
|
+
|
|
354
|
+
METRICS TRACKING:
|
|
355
|
+
- Rolling average of deployment times (last 100)
|
|
356
|
+
- Success/failure rates
|
|
357
|
+
- Agent type distribution
|
|
358
|
+
- Version migration patterns
|
|
359
|
+
- Error frequency analysis
|
|
360
|
+
|
|
361
|
+
This method demonstrates ETL-like processing:
|
|
362
|
+
1. Extract: Gather raw metrics from deployment results
|
|
363
|
+
2. Transform: Calculate averages, rates, and distributions
|
|
364
|
+
3. Load: Store in internal metrics structure for reporting
|
|
365
|
+
"""
|
|
366
|
+
# Update total deployment count
|
|
367
|
+
self._deployment_metrics['total_deployments'] += 1
|
|
368
|
+
|
|
369
|
+
# Track success/failure
|
|
370
|
+
if not results.get('errors'):
|
|
371
|
+
self._deployment_metrics['successful_deployments'] += 1
|
|
372
|
+
else:
|
|
373
|
+
self._deployment_metrics['failed_deployments'] += 1
|
|
374
|
+
|
|
375
|
+
# Update rolling average deployment time
|
|
376
|
+
self._deployment_metrics['deployment_times'].append(duration_ms)
|
|
377
|
+
if len(self._deployment_metrics['deployment_times']) > 100:
|
|
378
|
+
# Keep only last 100 for memory efficiency
|
|
379
|
+
self._deployment_metrics['deployment_times'] = \
|
|
380
|
+
self._deployment_metrics['deployment_times'][-100:]
|
|
381
|
+
|
|
382
|
+
# Calculate new average
|
|
383
|
+
if self._deployment_metrics['deployment_times']:
|
|
384
|
+
self._deployment_metrics['average_deployment_time_ms'] = \
|
|
385
|
+
sum(self._deployment_metrics['deployment_times']) / \
|
|
386
|
+
len(self._deployment_metrics['deployment_times'])
|
|
387
|
+
|
|
388
|
+
def get_deployment_metrics(self) -> Dict[str, Any]:
|
|
389
|
+
"""
|
|
390
|
+
Get current deployment metrics.
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
Dictionary containing:
|
|
394
|
+
- Total deployments and success rates
|
|
395
|
+
- Average deployment time
|
|
396
|
+
- Agent type distribution
|
|
397
|
+
- Migration statistics
|
|
398
|
+
- Error analysis
|
|
399
|
+
|
|
400
|
+
This demonstrates a metrics API endpoint that could be:
|
|
401
|
+
- Exposed via REST API for monitoring tools
|
|
402
|
+
- Pushed to time-series databases (Prometheus, InfluxDB)
|
|
403
|
+
- Used for dashboards and alerting
|
|
404
|
+
- Integrated with AI observability platforms
|
|
405
|
+
"""
|
|
406
|
+
success_rate = 0.0
|
|
407
|
+
if self._deployment_metrics['total_deployments'] > 0:
|
|
408
|
+
success_rate = (self._deployment_metrics['successful_deployments'] /
|
|
409
|
+
self._deployment_metrics['total_deployments']) * 100
|
|
410
|
+
|
|
411
|
+
return {
|
|
412
|
+
'total_deployments': self._deployment_metrics['total_deployments'],
|
|
413
|
+
'successful_deployments': self._deployment_metrics['successful_deployments'],
|
|
414
|
+
'failed_deployments': self._deployment_metrics['failed_deployments'],
|
|
415
|
+
'success_rate_percent': success_rate,
|
|
416
|
+
'average_deployment_time_ms': self._deployment_metrics['average_deployment_time_ms'],
|
|
417
|
+
'migrations_performed': self._deployment_metrics['migrations_performed'],
|
|
418
|
+
'agent_type_distribution': self._deployment_metrics['agent_type_counts'].copy(),
|
|
419
|
+
'version_migrations': self._deployment_metrics['version_migration_count'],
|
|
420
|
+
'error_distribution': self._deployment_metrics['deployment_errors'].copy(),
|
|
421
|
+
'recent_deployment_times': self._deployment_metrics['deployment_times'][-10:] # Last 10
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
def reset_metrics(self) -> None:
|
|
425
|
+
"""
|
|
426
|
+
Reset deployment metrics.
|
|
427
|
+
|
|
428
|
+
Useful for:
|
|
429
|
+
- Starting fresh metrics collection periods
|
|
430
|
+
- Testing and development
|
|
431
|
+
- Scheduled metric rotation (e.g., daily reset)
|
|
432
|
+
"""
|
|
433
|
+
self._deployment_metrics = {
|
|
434
|
+
'total_deployments': 0,
|
|
435
|
+
'successful_deployments': 0,
|
|
436
|
+
'failed_deployments': 0,
|
|
437
|
+
'migrations_performed': 0,
|
|
438
|
+
'average_deployment_time_ms': 0.0,
|
|
439
|
+
'deployment_times': [],
|
|
440
|
+
'agent_type_counts': {},
|
|
441
|
+
'version_migration_count': 0,
|
|
442
|
+
'template_validation_times': {},
|
|
443
|
+
'deployment_errors': {}
|
|
444
|
+
}
|
|
445
|
+
self.logger.info("Deployment metrics reset")
|
|
446
|
+
|
|
179
447
|
def _extract_version(self, content: str, version_marker: str) -> int:
|
|
180
448
|
"""
|
|
181
449
|
Extract version number from content.
|
|
@@ -223,18 +491,29 @@ class AgentDeploymentService:
|
|
|
223
491
|
version_string = self._format_version_display(agent_version)
|
|
224
492
|
|
|
225
493
|
# Build YAML frontmatter
|
|
494
|
+
# Check new format first (metadata.description), then old format
|
|
226
495
|
description = (
|
|
496
|
+
template_data.get('metadata', {}).get('description') or
|
|
227
497
|
template_data.get('configuration_fields', {}).get('description') or
|
|
228
498
|
template_data.get('description') or
|
|
229
499
|
'Agent for specialized tasks'
|
|
230
500
|
)
|
|
231
501
|
|
|
502
|
+
# Get tags from new format (metadata.tags) or old format
|
|
232
503
|
tags = (
|
|
504
|
+
template_data.get('metadata', {}).get('tags') or
|
|
233
505
|
template_data.get('configuration_fields', {}).get('tags') or
|
|
234
506
|
template_data.get('tags') or
|
|
235
507
|
[agent_name, 'mpm-framework']
|
|
236
508
|
)
|
|
237
509
|
|
|
510
|
+
# Get tools from capabilities.tools in new format
|
|
511
|
+
tools = (
|
|
512
|
+
template_data.get('capabilities', {}).get('tools') or
|
|
513
|
+
template_data.get('configuration_fields', {}).get('tools') or
|
|
514
|
+
["Read", "Write", "Edit", "Grep", "Glob", "LS"] # Default fallback
|
|
515
|
+
)
|
|
516
|
+
|
|
238
517
|
frontmatter = f"""---
|
|
239
518
|
name: {agent_name}
|
|
240
519
|
description: "{description}"
|
|
@@ -243,6 +522,7 @@ author: "{template_data.get('author', 'claude-mpm@anthropic.com')}"
|
|
|
243
522
|
created: "{datetime.now().isoformat()}Z"
|
|
244
523
|
updated: "{datetime.now().isoformat()}Z"
|
|
245
524
|
tags: {tags}
|
|
525
|
+
tools: {tools}
|
|
246
526
|
metadata:
|
|
247
527
|
base_version: "{self._format_version_display(base_version)}"
|
|
248
528
|
agent_version: "{self._format_version_display(agent_version)}"
|
|
@@ -265,6 +545,7 @@ metadata:
|
|
|
265
545
|
def _build_agent_yaml(self, agent_name: str, template_path: Path, base_agent_data: dict) -> str:
|
|
266
546
|
"""
|
|
267
547
|
Build a complete agent YAML file by combining base agent and template.
|
|
548
|
+
Only includes essential fields for Claude Code best practices.
|
|
268
549
|
|
|
269
550
|
Args:
|
|
270
551
|
agent_name: Name of the agent
|
|
@@ -275,92 +556,80 @@ metadata:
|
|
|
275
556
|
Complete agent YAML content
|
|
276
557
|
"""
|
|
277
558
|
import json
|
|
278
|
-
from datetime import datetime
|
|
279
559
|
|
|
280
560
|
# Read template JSON
|
|
281
561
|
template_data = json.loads(template_path.read_text())
|
|
282
562
|
|
|
283
|
-
# Extract
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
base_version = self._parse_version(base_agent_data.get('base_version') or base_agent_data.get('version', 0))
|
|
563
|
+
# Extract capabilities
|
|
564
|
+
capabilities = template_data.get('capabilities', {})
|
|
565
|
+
metadata = template_data.get('metadata', {})
|
|
287
566
|
|
|
288
|
-
#
|
|
567
|
+
# Extract version information
|
|
568
|
+
agent_version = self._parse_version(template_data.get('agent_version') or template_data.get('version', 0))
|
|
289
569
|
version_string = self._format_version_display(agent_version)
|
|
290
570
|
|
|
291
|
-
#
|
|
292
|
-
|
|
571
|
+
# Get tools list
|
|
572
|
+
tools = capabilities.get('tools', [])
|
|
573
|
+
tools_str = ', '.join(tools) if tools else 'Read, Write, Edit, Grep, Glob, LS'
|
|
574
|
+
|
|
575
|
+
# Get description
|
|
576
|
+
description = (
|
|
577
|
+
metadata.get('description') or
|
|
578
|
+
template_data.get('description') or
|
|
579
|
+
f'{agent_name.title()} agent for specialized tasks'
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
# Get priority based on agent type
|
|
583
|
+
priority_map = {
|
|
584
|
+
'security': 'high',
|
|
585
|
+
'qa': 'high',
|
|
586
|
+
'engineer': 'high',
|
|
587
|
+
'documentation': 'medium',
|
|
588
|
+
'research': 'medium',
|
|
589
|
+
'ops': 'high',
|
|
590
|
+
'data_engineer': 'medium',
|
|
591
|
+
'version_control': 'high'
|
|
592
|
+
}
|
|
593
|
+
priority = priority_map.get(agent_name, 'medium')
|
|
293
594
|
|
|
294
|
-
#
|
|
295
|
-
|
|
595
|
+
# Get model
|
|
596
|
+
model = capabilities.get('model', 'claude-3-5-sonnet-20241022')
|
|
296
597
|
|
|
297
|
-
#
|
|
598
|
+
# Get temperature
|
|
599
|
+
temperature = capabilities.get('temperature', 0.3)
|
|
600
|
+
|
|
601
|
+
# Build clean YAML frontmatter with only essential fields
|
|
298
602
|
yaml_content = f"""---
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
description: "{config_fields.get('description', '')}"
|
|
603
|
+
name: {agent_name}
|
|
604
|
+
description: "{description}"
|
|
302
605
|
version: "{version_string}"
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
dangerous_tools: {str(config_fields.get('dangerous_tools', False)).lower()}
|
|
324
|
-
review_required: {str(config_fields.get('review_required', False)).lower()}
|
|
325
|
-
|
|
326
|
-
# Resource Management
|
|
327
|
-
memory_limit: {config_fields.get('memory_limit', 2048)}
|
|
328
|
-
cpu_limit: {config_fields.get('cpu_limit', 50)}
|
|
329
|
-
execution_timeout: {config_fields.get('timeout', 600)}
|
|
330
|
-
|
|
331
|
-
# When/Why/What sections extracted from template
|
|
332
|
-
when_to_use:
|
|
333
|
-
{self._format_yaml_list(narrative_fields.get('when_to_use', []), 2)}
|
|
334
|
-
|
|
335
|
-
rationale:
|
|
336
|
-
specialized_knowledge:
|
|
337
|
-
{self._format_yaml_list(narrative_fields.get('specialized_knowledge', []), 4)}
|
|
338
|
-
unique_capabilities:
|
|
339
|
-
{self._format_yaml_list(narrative_fields.get('unique_capabilities', []), 4)}
|
|
340
|
-
|
|
341
|
-
capabilities:
|
|
342
|
-
primary_role: "{config_fields.get('primary_role', '')}"
|
|
343
|
-
specializations: {config_fields.get('specializations', [])}
|
|
344
|
-
authority: "{config_fields.get('authority', '')}"
|
|
345
|
-
|
|
346
|
-
# Agent Metadata
|
|
347
|
-
metadata:
|
|
348
|
-
source: "claude-mpm"
|
|
349
|
-
template_version: "{self._format_version_display(agent_version)}"
|
|
350
|
-
base_version: "{self._format_version_display(base_version)}"
|
|
351
|
-
deployment_type: "system"
|
|
352
|
-
|
|
353
|
-
...
|
|
354
|
-
---
|
|
355
|
-
|
|
356
|
-
# System Prompt
|
|
357
|
-
|
|
358
|
-
"""
|
|
606
|
+
tools: {tools_str}
|
|
607
|
+
priority: {priority}
|
|
608
|
+
model: {model}
|
|
609
|
+
temperature: {temperature}"""
|
|
610
|
+
|
|
611
|
+
# Add allowed_tools if present
|
|
612
|
+
if 'allowed_tools' in capabilities:
|
|
613
|
+
yaml_content += f"\nallowed_tools: {json.dumps(capabilities['allowed_tools'])}"
|
|
614
|
+
|
|
615
|
+
# Add disallowed_tools if present
|
|
616
|
+
if 'disallowed_tools' in capabilities:
|
|
617
|
+
yaml_content += f"\ndisallowed_tools: {json.dumps(capabilities['disallowed_tools'])}"
|
|
618
|
+
|
|
619
|
+
yaml_content += "\n---\n"
|
|
620
|
+
|
|
621
|
+
# Get instructions from template
|
|
622
|
+
instructions = (
|
|
623
|
+
template_data.get('instructions') or
|
|
624
|
+
base_agent_data.get('narrative_fields', {}).get('instructions', '')
|
|
625
|
+
)
|
|
359
626
|
|
|
360
|
-
# Add
|
|
361
|
-
|
|
362
|
-
if
|
|
363
|
-
yaml_content +=
|
|
627
|
+
# Add base instructions if not already included
|
|
628
|
+
base_instructions = base_agent_data.get('narrative_fields', {}).get('instructions', '')
|
|
629
|
+
if base_instructions and base_instructions not in instructions:
|
|
630
|
+
yaml_content += base_instructions + "\n\n---\n\n"
|
|
631
|
+
|
|
632
|
+
yaml_content += instructions
|
|
364
633
|
|
|
365
634
|
return yaml_content
|
|
366
635
|
|
|
@@ -421,17 +690,74 @@ metadata:
|
|
|
421
690
|
# Override with template-specific configuration
|
|
422
691
|
merged.update(template_config)
|
|
423
692
|
|
|
693
|
+
# Also merge in capabilities from new format if not already in config
|
|
694
|
+
capabilities = template_data.get('capabilities', {})
|
|
695
|
+
if capabilities:
|
|
696
|
+
# Map capabilities fields to configuration fields
|
|
697
|
+
if 'tools' not in merged and 'tools' in capabilities:
|
|
698
|
+
merged['tools'] = capabilities['tools']
|
|
699
|
+
if 'max_tokens' not in merged and 'max_tokens' in capabilities:
|
|
700
|
+
merged['max_tokens'] = capabilities['max_tokens']
|
|
701
|
+
if 'temperature' not in merged and 'temperature' in capabilities:
|
|
702
|
+
merged['temperature'] = capabilities['temperature']
|
|
703
|
+
if 'timeout' not in merged and 'timeout' in capabilities:
|
|
704
|
+
merged['timeout'] = capabilities['timeout']
|
|
705
|
+
if 'memory_limit' not in merged and 'memory_limit' in capabilities:
|
|
706
|
+
merged['memory_limit'] = capabilities['memory_limit']
|
|
707
|
+
if 'cpu_limit' not in merged and 'cpu_limit' in capabilities:
|
|
708
|
+
merged['cpu_limit'] = capabilities['cpu_limit']
|
|
709
|
+
if 'network_access' not in merged and 'network_access' in capabilities:
|
|
710
|
+
merged['network_access'] = capabilities['network_access']
|
|
711
|
+
if 'model' not in merged and 'model' in capabilities:
|
|
712
|
+
merged['model'] = capabilities['model']
|
|
713
|
+
|
|
714
|
+
# Also check metadata for description and tags in new format
|
|
715
|
+
metadata = template_data.get('metadata', {})
|
|
716
|
+
if metadata:
|
|
717
|
+
if 'description' not in merged and 'description' in metadata:
|
|
718
|
+
merged['description'] = metadata['description']
|
|
719
|
+
if 'tags' not in merged and 'tags' in metadata:
|
|
720
|
+
merged['tags'] = metadata['tags']
|
|
721
|
+
|
|
424
722
|
return merged
|
|
425
723
|
|
|
426
724
|
def set_claude_environment(self, config_dir: Optional[Path] = None) -> Dict[str, str]:
|
|
427
725
|
"""
|
|
428
726
|
Set Claude environment variables for agent discovery.
|
|
429
727
|
|
|
728
|
+
OPERATIONAL PURPOSE:
|
|
729
|
+
Claude Code discovers agents through environment variables that
|
|
730
|
+
point to configuration directories. This method ensures proper
|
|
731
|
+
environment setup for agent runtime discovery.
|
|
732
|
+
|
|
733
|
+
ENVIRONMENT VARIABLES SET:
|
|
734
|
+
1. CLAUDE_CONFIG_DIR: Root configuration directory path
|
|
735
|
+
2. CLAUDE_MAX_PARALLEL_SUBAGENTS: Concurrency limit (default: 5)
|
|
736
|
+
3. CLAUDE_TIMEOUT: Agent execution timeout (default: 600s)
|
|
737
|
+
|
|
738
|
+
DEPLOYMENT CONSIDERATIONS:
|
|
739
|
+
- Call after agent deployment for immediate availability
|
|
740
|
+
- Environment changes affect current process and children
|
|
741
|
+
- Does not persist across system restarts
|
|
742
|
+
- Add to shell profile for permanent configuration
|
|
743
|
+
|
|
744
|
+
TROUBLESHOOTING:
|
|
745
|
+
- Verify with: echo $CLAUDE_CONFIG_DIR
|
|
746
|
+
- Check agent discovery: ls $CLAUDE_CONFIG_DIR/agents/
|
|
747
|
+
- Monitor timeout issues in production
|
|
748
|
+
- Adjust parallel limits based on system resources
|
|
749
|
+
|
|
750
|
+
PERFORMANCE TUNING:
|
|
751
|
+
- Increase parallel agents for CPU-bound tasks
|
|
752
|
+
- Reduce for memory-constrained environments
|
|
753
|
+
- Balance timeout with longest expected operations
|
|
754
|
+
- Monitor resource usage during parallel execution
|
|
755
|
+
|
|
430
756
|
Args:
|
|
431
757
|
config_dir: Claude configuration directory (default: .claude/)
|
|
432
758
|
|
|
433
759
|
Returns:
|
|
434
|
-
Dictionary of environment variables set
|
|
760
|
+
Dictionary of environment variables set for verification
|
|
435
761
|
"""
|
|
436
762
|
if not config_dir:
|
|
437
763
|
config_dir = Path.cwd() / Paths.CLAUDE_CONFIG_DIR.value
|
|
@@ -458,11 +784,46 @@ metadata:
|
|
|
458
784
|
"""
|
|
459
785
|
Verify agent deployment and Claude configuration.
|
|
460
786
|
|
|
787
|
+
OPERATIONAL PURPOSE:
|
|
788
|
+
Post-deployment verification ensures agents are correctly deployed
|
|
789
|
+
and discoverable by Claude Code. Critical for deployment validation
|
|
790
|
+
and troubleshooting runtime issues.
|
|
791
|
+
|
|
792
|
+
VERIFICATION CHECKS:
|
|
793
|
+
1. Configuration directory exists and is accessible
|
|
794
|
+
2. Agents directory contains expected YAML files
|
|
795
|
+
3. Agent files have valid YAML frontmatter
|
|
796
|
+
4. Version format is current (identifies migration needs)
|
|
797
|
+
5. Environment variables are properly set
|
|
798
|
+
|
|
799
|
+
MONITORING INTEGRATION:
|
|
800
|
+
- Call after deployment for health checks
|
|
801
|
+
- Include in deployment pipelines
|
|
802
|
+
- Log results for audit trails
|
|
803
|
+
- Alert on missing agents or errors
|
|
804
|
+
|
|
805
|
+
TROUBLESHOOTING GUIDE:
|
|
806
|
+
- Missing config_dir: Check deployment target path
|
|
807
|
+
- No agents found: Verify deployment completed
|
|
808
|
+
- Migration needed: Run with force_rebuild
|
|
809
|
+
- Environment warnings: Call set_claude_environment()
|
|
810
|
+
|
|
811
|
+
RESULT INTERPRETATION:
|
|
812
|
+
- agents_found: Successfully deployed agents
|
|
813
|
+
- agents_needing_migration: Require version update
|
|
814
|
+
- warnings: Non-critical issues to address
|
|
815
|
+
- environment: Current runtime configuration
|
|
816
|
+
|
|
461
817
|
Args:
|
|
462
818
|
config_dir: Claude configuration directory (default: .claude/)
|
|
463
819
|
|
|
464
820
|
Returns:
|
|
465
|
-
Verification results
|
|
821
|
+
Verification results dictionary:
|
|
822
|
+
- config_dir: Checked directory path
|
|
823
|
+
- agents_found: List of discovered agents with metadata
|
|
824
|
+
- agents_needing_migration: Agents with old version format
|
|
825
|
+
- environment: Current environment variables
|
|
826
|
+
- warnings: List of potential issues
|
|
466
827
|
"""
|
|
467
828
|
if not config_dir:
|
|
468
829
|
config_dir = Path.cwd() / ".claude"
|
|
@@ -487,7 +848,7 @@ metadata:
|
|
|
487
848
|
return results
|
|
488
849
|
|
|
489
850
|
# List deployed agents
|
|
490
|
-
agent_files = list(agents_dir.glob("*.
|
|
851
|
+
agent_files = list(agents_dir.glob("*.yaml"))
|
|
491
852
|
for agent_file in agent_files:
|
|
492
853
|
try:
|
|
493
854
|
# Read first few lines to get agent name from YAML
|
|
@@ -592,13 +953,40 @@ metadata:
|
|
|
592
953
|
"""
|
|
593
954
|
Check if a deployed agent needs to be updated.
|
|
594
955
|
|
|
956
|
+
OPERATIONAL LOGIC:
|
|
957
|
+
1. Verifies agent is system-managed (claude-mpm authored)
|
|
958
|
+
2. Extracts version from deployed YAML frontmatter
|
|
959
|
+
3. Detects old version formats requiring migration
|
|
960
|
+
4. Compares semantic versions for update decision
|
|
961
|
+
5. Returns detailed reason for update/skip decision
|
|
962
|
+
|
|
963
|
+
VERSION MIGRATION STRATEGY:
|
|
964
|
+
- Old serial format (0002-0005) -> Semantic (2.5.0)
|
|
965
|
+
- Missing versions -> Force update to latest
|
|
966
|
+
- Non-semantic formats -> Trigger migration
|
|
967
|
+
- Preserves user modifications (non-system agents)
|
|
968
|
+
|
|
969
|
+
PERFORMANCE OPTIMIZATION:
|
|
970
|
+
- Early exit for non-system agents
|
|
971
|
+
- Regex compilation cached by Python
|
|
972
|
+
- Minimal file reads (frontmatter only)
|
|
973
|
+
- Version comparison without full parse
|
|
974
|
+
|
|
975
|
+
ERROR RECOVERY:
|
|
976
|
+
- Assumes update needed on parse failures
|
|
977
|
+
- Logs warnings for investigation
|
|
978
|
+
- Never blocks deployment pipeline
|
|
979
|
+
- Safe fallback to force update
|
|
980
|
+
|
|
595
981
|
Args:
|
|
596
982
|
deployed_file: Path to the deployed agent file
|
|
597
983
|
template_file: Path to the template file
|
|
598
|
-
current_base_version: Current base agent version
|
|
984
|
+
current_base_version: Current base agent version (unused in new strategy)
|
|
599
985
|
|
|
600
986
|
Returns:
|
|
601
|
-
Tuple of (needs_update, reason)
|
|
987
|
+
Tuple of (needs_update: bool, reason: str)
|
|
988
|
+
- needs_update: True if agent should be redeployed
|
|
989
|
+
- reason: Human-readable explanation for decision
|
|
602
990
|
"""
|
|
603
991
|
try:
|
|
604
992
|
# Read deployed agent content
|
|
@@ -713,7 +1101,7 @@ metadata:
|
|
|
713
1101
|
return results
|
|
714
1102
|
|
|
715
1103
|
# Remove system agents only (identified by claude-mpm author)
|
|
716
|
-
agent_files = list(agents_dir.glob("*.
|
|
1104
|
+
agent_files = list(agents_dir.glob("*.yaml"))
|
|
717
1105
|
|
|
718
1106
|
for agent_file in agent_files:
|
|
719
1107
|
try:
|