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,497 @@
|
|
|
1
|
+
"""Template editing operations for configure command.
|
|
2
|
+
|
|
3
|
+
WHY: Agent templates control agent behavior, tools, and capabilities. Users need
|
|
4
|
+
to customize templates without manually editing JSON files. This module provides
|
|
5
|
+
interactive template editing with safety features.
|
|
6
|
+
|
|
7
|
+
DESIGN DECISIONS:
|
|
8
|
+
- Distinguish system templates (read-only) from custom templates (editable)
|
|
9
|
+
- Support external editor integration (respects $EDITOR environment variable)
|
|
10
|
+
- Field-level editing with dot notation (e.g., "config.timeout")
|
|
11
|
+
- Automatic backup and validation
|
|
12
|
+
- Visual diff display for template changes
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import os
|
|
17
|
+
import subprocess
|
|
18
|
+
import tempfile
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Dict, List
|
|
21
|
+
|
|
22
|
+
from rich.console import Console
|
|
23
|
+
from rich.prompt import Confirm, Prompt
|
|
24
|
+
from rich.syntax import Syntax
|
|
25
|
+
from rich.text import Text
|
|
26
|
+
|
|
27
|
+
from .configure_models import AgentConfig
|
|
28
|
+
from .configure_paths import get_agent_template_path
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TemplateEditor:
|
|
32
|
+
"""Template CRUD operations for agent configuration.
|
|
33
|
+
|
|
34
|
+
This class handles:
|
|
35
|
+
- Interactive template editing with external editors
|
|
36
|
+
- Field-level modifications (add/modify/remove)
|
|
37
|
+
- System vs custom template management
|
|
38
|
+
- Template reset and copy operations
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
console: Console,
|
|
44
|
+
agent_manager, # SimpleAgentManager instance
|
|
45
|
+
current_scope: str,
|
|
46
|
+
project_dir: Path,
|
|
47
|
+
):
|
|
48
|
+
"""Initialize template editor.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
console: Rich console for output
|
|
52
|
+
agent_manager: SimpleAgentManager for state and paths
|
|
53
|
+
current_scope: Current configuration scope (project/user)
|
|
54
|
+
project_dir: Current project directory
|
|
55
|
+
"""
|
|
56
|
+
self.console = console
|
|
57
|
+
self.agent_manager = agent_manager
|
|
58
|
+
self.current_scope = current_scope
|
|
59
|
+
self.project_dir = project_dir
|
|
60
|
+
|
|
61
|
+
def get_agent_template_path(self, agent_name: str) -> Path:
|
|
62
|
+
"""Get the path to an agent's template file.
|
|
63
|
+
|
|
64
|
+
Resolves template path based on current scope:
|
|
65
|
+
- Project scope: .claude-mpm/agents in project dir
|
|
66
|
+
- User scope: .claude-mpm/agents in home dir
|
|
67
|
+
- Fallback: System templates directory
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
agent_name: Name of the agent
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Path to agent template file
|
|
74
|
+
"""
|
|
75
|
+
return get_agent_template_path(
|
|
76
|
+
agent_name,
|
|
77
|
+
self.current_scope,
|
|
78
|
+
self.project_dir,
|
|
79
|
+
self.agent_manager.templates_dir,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def customize_agent_template(self, agents: List[AgentConfig]) -> None:
|
|
83
|
+
"""Customize agent JSON template (entry point).
|
|
84
|
+
|
|
85
|
+
Prompts for agent ID and launches the template editor.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
agents: List of available agents
|
|
89
|
+
"""
|
|
90
|
+
agent_id = Prompt.ask("Enter agent ID to customize")
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
idx = int(agent_id) - 1
|
|
94
|
+
if 0 <= idx < len(agents):
|
|
95
|
+
agent = agents[idx]
|
|
96
|
+
self.edit_agent_template(agent)
|
|
97
|
+
else:
|
|
98
|
+
self.console.print("[red]Invalid agent ID.[/red]")
|
|
99
|
+
Prompt.ask("Press Enter to continue")
|
|
100
|
+
except ValueError:
|
|
101
|
+
self.console.print("[red]Invalid input. Please enter a number.[/red]")
|
|
102
|
+
Prompt.ask("Press Enter to continue")
|
|
103
|
+
|
|
104
|
+
def edit_agent_template(self, agent: AgentConfig) -> None:
|
|
105
|
+
"""Edit an agent's JSON template (main editor interface).
|
|
106
|
+
|
|
107
|
+
This is the most complex method (CC=18) that:
|
|
108
|
+
- Loads existing template or creates minimal template
|
|
109
|
+
- Displays template with syntax highlighting
|
|
110
|
+
- Provides editing options based on template type (system vs custom)
|
|
111
|
+
- Routes to appropriate sub-operations
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
agent: Agent configuration to edit
|
|
115
|
+
"""
|
|
116
|
+
self.console.clear()
|
|
117
|
+
self.console.print(f"[bold]Editing template for: {agent.name}[/bold]\n")
|
|
118
|
+
|
|
119
|
+
# Get current template
|
|
120
|
+
template_path = self.get_agent_template_path(agent.name)
|
|
121
|
+
|
|
122
|
+
if template_path.exists():
|
|
123
|
+
with template_path.open() as f:
|
|
124
|
+
template = json.load(f)
|
|
125
|
+
is_system = str(template_path).startswith(
|
|
126
|
+
str(self.agent_manager.templates_dir)
|
|
127
|
+
)
|
|
128
|
+
else:
|
|
129
|
+
# Create a minimal template structure based on system templates
|
|
130
|
+
template = {
|
|
131
|
+
"schema_version": "1.2.0",
|
|
132
|
+
"agent_id": agent.name,
|
|
133
|
+
"agent_version": "1.0.0",
|
|
134
|
+
"agent_type": agent.name.replace("-", "_"),
|
|
135
|
+
"metadata": {
|
|
136
|
+
"name": agent.name.replace("-", " ").title() + " Agent",
|
|
137
|
+
"description": agent.description,
|
|
138
|
+
"tags": [agent.name],
|
|
139
|
+
"author": "Custom",
|
|
140
|
+
"created_at": "",
|
|
141
|
+
"updated_at": "",
|
|
142
|
+
},
|
|
143
|
+
"capabilities": {
|
|
144
|
+
"model": "opus",
|
|
145
|
+
"tools": (
|
|
146
|
+
agent.dependencies
|
|
147
|
+
if agent.dependencies
|
|
148
|
+
else ["Read", "Write", "Edit", "Bash"]
|
|
149
|
+
),
|
|
150
|
+
},
|
|
151
|
+
"instructions": {
|
|
152
|
+
"base_template": "BASE_AGENT_TEMPLATE.md",
|
|
153
|
+
"custom_instructions": "",
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
is_system = False
|
|
157
|
+
|
|
158
|
+
# Display current template
|
|
159
|
+
if is_system:
|
|
160
|
+
self.console.print(
|
|
161
|
+
"[yellow]Viewing SYSTEM template (read-only). Customization will create a local copy.[/yellow]\n"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
self.console.print("[bold]Current Template:[/bold]")
|
|
165
|
+
# Truncate for display if too large
|
|
166
|
+
display_template = template.copy()
|
|
167
|
+
if (
|
|
168
|
+
"instructions" in display_template
|
|
169
|
+
and isinstance(display_template["instructions"], dict)
|
|
170
|
+
and (
|
|
171
|
+
"custom_instructions" in display_template["instructions"]
|
|
172
|
+
and len(str(display_template["instructions"]["custom_instructions"]))
|
|
173
|
+
> 200
|
|
174
|
+
)
|
|
175
|
+
):
|
|
176
|
+
display_template["instructions"]["custom_instructions"] = (
|
|
177
|
+
display_template["instructions"]["custom_instructions"][:200] + "..."
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
json_str = json.dumps(display_template, indent=2)
|
|
181
|
+
# Limit display to first 50 lines for readability
|
|
182
|
+
lines = json_str.split("\n")
|
|
183
|
+
if len(lines) > 50:
|
|
184
|
+
json_str = "\n".join(lines[:50]) + "\n... (truncated for display)"
|
|
185
|
+
|
|
186
|
+
syntax = Syntax(json_str, "json", theme="monokai", line_numbers=True)
|
|
187
|
+
self.console.print(syntax)
|
|
188
|
+
self.console.print()
|
|
189
|
+
|
|
190
|
+
# Editing options
|
|
191
|
+
self.console.print("[bold]Editing Options:[/bold]")
|
|
192
|
+
if not is_system:
|
|
193
|
+
text_1 = Text(" ")
|
|
194
|
+
text_1.append("[1]", style="bold blue")
|
|
195
|
+
text_1.append(" Edit in external editor")
|
|
196
|
+
self.console.print(text_1)
|
|
197
|
+
|
|
198
|
+
text_2 = Text(" ")
|
|
199
|
+
text_2.append("[2]", style="bold blue")
|
|
200
|
+
text_2.append(" Add/modify a field")
|
|
201
|
+
self.console.print(text_2)
|
|
202
|
+
|
|
203
|
+
text_3 = Text(" ")
|
|
204
|
+
text_3.append("[3]", style="bold blue")
|
|
205
|
+
text_3.append(" Remove a field")
|
|
206
|
+
self.console.print(text_3)
|
|
207
|
+
|
|
208
|
+
text_4 = Text(" ")
|
|
209
|
+
text_4.append("[4]", style="bold blue")
|
|
210
|
+
text_4.append(" Reset to defaults")
|
|
211
|
+
self.console.print(text_4)
|
|
212
|
+
else:
|
|
213
|
+
text_1 = Text(" ")
|
|
214
|
+
text_1.append("[1]", style="bold blue")
|
|
215
|
+
text_1.append(" Create customized copy")
|
|
216
|
+
self.console.print(text_1)
|
|
217
|
+
|
|
218
|
+
text_2 = Text(" ")
|
|
219
|
+
text_2.append("[2]", style="bold blue")
|
|
220
|
+
text_2.append(" View full template")
|
|
221
|
+
self.console.print(text_2)
|
|
222
|
+
|
|
223
|
+
text_b = Text(" ")
|
|
224
|
+
text_b.append("[b]", style="bold blue")
|
|
225
|
+
text_b.append(" Back")
|
|
226
|
+
self.console.print(text_b)
|
|
227
|
+
|
|
228
|
+
self.console.print()
|
|
229
|
+
|
|
230
|
+
choice = Prompt.ask("[bold blue]Select an option[/bold blue]", default="b")
|
|
231
|
+
|
|
232
|
+
if is_system:
|
|
233
|
+
if choice == "1":
|
|
234
|
+
# Create a customized copy
|
|
235
|
+
self.create_custom_template_copy(agent, template)
|
|
236
|
+
elif choice == "2":
|
|
237
|
+
# View full template
|
|
238
|
+
self.view_full_template(template)
|
|
239
|
+
elif choice == "1":
|
|
240
|
+
self.edit_in_external_editor(template_path, template)
|
|
241
|
+
elif choice == "2":
|
|
242
|
+
self.modify_template_field(template, template_path)
|
|
243
|
+
elif choice == "3":
|
|
244
|
+
self.remove_template_field(template, template_path)
|
|
245
|
+
elif choice == "4":
|
|
246
|
+
self.reset_template(agent, template_path)
|
|
247
|
+
|
|
248
|
+
if choice != "b":
|
|
249
|
+
Prompt.ask("Press Enter to continue")
|
|
250
|
+
|
|
251
|
+
def edit_in_external_editor(self, template_path: Path, template: Dict) -> None:
|
|
252
|
+
"""Open template in external editor.
|
|
253
|
+
|
|
254
|
+
Uses $EDITOR environment variable (defaults to nano).
|
|
255
|
+
Saves changes back to template file after editing.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
template_path: Path to save edited template
|
|
259
|
+
template: Current template dict to edit
|
|
260
|
+
"""
|
|
261
|
+
# Write current template to temp file
|
|
262
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
263
|
+
json.dump(template, f, indent=2)
|
|
264
|
+
temp_path = f.name
|
|
265
|
+
|
|
266
|
+
# Get editor from environment
|
|
267
|
+
editor = os.environ.get("EDITOR", "nano")
|
|
268
|
+
|
|
269
|
+
try:
|
|
270
|
+
# Open in editor
|
|
271
|
+
subprocess.call([editor, temp_path])
|
|
272
|
+
|
|
273
|
+
# Read back the edited content
|
|
274
|
+
with open(temp_path) as f:
|
|
275
|
+
new_template = json.load(f)
|
|
276
|
+
|
|
277
|
+
# Save to actual template path
|
|
278
|
+
with template_path.open("w") as f:
|
|
279
|
+
json.dump(new_template, f, indent=2)
|
|
280
|
+
|
|
281
|
+
self.console.print("[green]Template updated successfully![/green]")
|
|
282
|
+
|
|
283
|
+
except Exception as e:
|
|
284
|
+
self.console.print(f"[red]Error editing template: {e}[/red]")
|
|
285
|
+
finally:
|
|
286
|
+
# Clean up temp file
|
|
287
|
+
Path(temp_path).unlink(missing_ok=True)
|
|
288
|
+
|
|
289
|
+
def modify_template_field(self, template: Dict, template_path: Path) -> None:
|
|
290
|
+
"""Add or modify a field in the template.
|
|
291
|
+
|
|
292
|
+
Supports dot notation for nested fields (e.g., "config.timeout").
|
|
293
|
+
Accepts JSON-formatted values.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
template: Template dict to modify
|
|
297
|
+
template_path: Path to save modified template
|
|
298
|
+
"""
|
|
299
|
+
field_name = Prompt.ask(
|
|
300
|
+
"Enter field name (use dot notation for nested, e.g., 'config.timeout')"
|
|
301
|
+
)
|
|
302
|
+
field_value = Prompt.ask("Enter field value (JSON format)")
|
|
303
|
+
|
|
304
|
+
try:
|
|
305
|
+
# Parse the value as JSON
|
|
306
|
+
value = json.loads(field_value)
|
|
307
|
+
|
|
308
|
+
# Navigate to the field location
|
|
309
|
+
parts = field_name.split(".")
|
|
310
|
+
current = template
|
|
311
|
+
|
|
312
|
+
for part in parts[:-1]:
|
|
313
|
+
if part not in current:
|
|
314
|
+
current[part] = {}
|
|
315
|
+
current = current[part]
|
|
316
|
+
|
|
317
|
+
# Set the value
|
|
318
|
+
current[parts[-1]] = value
|
|
319
|
+
|
|
320
|
+
# Save the template
|
|
321
|
+
with template_path.open("w") as f:
|
|
322
|
+
json.dump(template, f, indent=2)
|
|
323
|
+
|
|
324
|
+
self.console.print(
|
|
325
|
+
f"[green]Field '{field_name}' updated successfully![/green]"
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
except json.JSONDecodeError:
|
|
329
|
+
self.console.print("[red]Invalid JSON value. Please try again.[/red]")
|
|
330
|
+
except Exception as e:
|
|
331
|
+
self.console.print(f"[red]Error updating field: {e}[/red]")
|
|
332
|
+
|
|
333
|
+
def remove_template_field(self, template: Dict, template_path: Path) -> None:
|
|
334
|
+
"""Remove a field from the template.
|
|
335
|
+
|
|
336
|
+
Supports dot notation for nested fields.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
template: Template dict to modify
|
|
340
|
+
template_path: Path to save modified template
|
|
341
|
+
"""
|
|
342
|
+
field_name = Prompt.ask(
|
|
343
|
+
"Enter field name to remove (use dot notation for nested)"
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
try:
|
|
347
|
+
# Navigate to the field location
|
|
348
|
+
parts = field_name.split(".")
|
|
349
|
+
current = template
|
|
350
|
+
|
|
351
|
+
for part in parts[:-1]:
|
|
352
|
+
if part not in current:
|
|
353
|
+
raise KeyError(f"Field '{field_name}' not found")
|
|
354
|
+
current = current[part]
|
|
355
|
+
|
|
356
|
+
# Remove the field
|
|
357
|
+
if parts[-1] in current:
|
|
358
|
+
del current[parts[-1]]
|
|
359
|
+
|
|
360
|
+
# Save the template
|
|
361
|
+
with template_path.open("w") as f:
|
|
362
|
+
json.dump(template, f, indent=2)
|
|
363
|
+
|
|
364
|
+
self.console.print(
|
|
365
|
+
f"[green]Field '{field_name}' removed successfully![/green]"
|
|
366
|
+
)
|
|
367
|
+
else:
|
|
368
|
+
self.console.print(f"[red]Field '{field_name}' not found.[/red]")
|
|
369
|
+
|
|
370
|
+
except Exception as e:
|
|
371
|
+
self.console.print(f"[red]Error removing field: {e}[/red]")
|
|
372
|
+
|
|
373
|
+
def reset_template(self, agent: AgentConfig, template_path: Path) -> None:
|
|
374
|
+
"""Reset template to defaults.
|
|
375
|
+
|
|
376
|
+
Removes custom template file, reverting to system template.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
agent: Agent configuration
|
|
380
|
+
template_path: Path to custom template (will be deleted)
|
|
381
|
+
"""
|
|
382
|
+
if Confirm.ask(f"[yellow]Reset '{agent.name}' template to defaults?[/yellow]"):
|
|
383
|
+
# Remove custom template file
|
|
384
|
+
template_path.unlink(missing_ok=True)
|
|
385
|
+
self.console.print(
|
|
386
|
+
f"[green]Template for '{agent.name}' reset to defaults![/green]"
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
def create_custom_template_copy(self, agent: AgentConfig, template: Dict) -> None:
|
|
390
|
+
"""Create a customized copy of a system template.
|
|
391
|
+
|
|
392
|
+
Copies system template to project/user config directory for editing.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
agent: Agent configuration
|
|
396
|
+
template: System template to copy
|
|
397
|
+
"""
|
|
398
|
+
if self.current_scope == "project":
|
|
399
|
+
config_dir = self.project_dir / ".claude-mpm" / "agents"
|
|
400
|
+
else:
|
|
401
|
+
config_dir = Path.home() / ".claude-mpm" / "agents"
|
|
402
|
+
|
|
403
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
404
|
+
custom_path = config_dir / f"{agent.name}.json"
|
|
405
|
+
|
|
406
|
+
if custom_path.exists() and not Confirm.ask(
|
|
407
|
+
"[yellow]Custom template already exists. Overwrite?[/yellow]"
|
|
408
|
+
):
|
|
409
|
+
return
|
|
410
|
+
|
|
411
|
+
# Save the template copy
|
|
412
|
+
with custom_path.open("w") as f:
|
|
413
|
+
json.dump(template, f, indent=2)
|
|
414
|
+
|
|
415
|
+
self.console.print(f"[green]Created custom template at: {custom_path}[/green]")
|
|
416
|
+
self.console.print("[green]You can now edit this template.[/green]")
|
|
417
|
+
|
|
418
|
+
def view_full_template(self, template: Dict) -> None:
|
|
419
|
+
"""View the full template without truncation.
|
|
420
|
+
|
|
421
|
+
Uses pager for long templates.
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
template: Template dict to display
|
|
425
|
+
"""
|
|
426
|
+
self.console.clear()
|
|
427
|
+
self.console.print("[bold]Full Template View:[/bold]\n")
|
|
428
|
+
|
|
429
|
+
json_str = json.dumps(template, indent=2)
|
|
430
|
+
syntax = Syntax(json_str, "json", theme="monokai", line_numbers=True)
|
|
431
|
+
|
|
432
|
+
# Use pager for long content
|
|
433
|
+
with self.console.pager():
|
|
434
|
+
self.console.print(syntax)
|
|
435
|
+
|
|
436
|
+
def reset_agent_defaults(self, agents: List[AgentConfig]) -> None:
|
|
437
|
+
"""Reset an agent to default enabled state and remove custom template.
|
|
438
|
+
|
|
439
|
+
This method:
|
|
440
|
+
- Prompts for agent ID
|
|
441
|
+
- Resets agent to enabled state
|
|
442
|
+
- Removes any custom template overrides
|
|
443
|
+
- Shows success/error messages
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
agents: List of available agents
|
|
447
|
+
"""
|
|
448
|
+
agent_id = Prompt.ask("Enter agent ID to reset to defaults")
|
|
449
|
+
|
|
450
|
+
try:
|
|
451
|
+
idx = int(agent_id) - 1
|
|
452
|
+
if 0 <= idx < len(agents):
|
|
453
|
+
agent = agents[idx]
|
|
454
|
+
|
|
455
|
+
# Confirm the reset action
|
|
456
|
+
if not Confirm.ask(
|
|
457
|
+
f"[yellow]Reset '{agent.name}' to defaults? This will:[/yellow]\n"
|
|
458
|
+
" - Enable the agent\n"
|
|
459
|
+
" - Remove custom template (if any)\n"
|
|
460
|
+
"[yellow]Continue?[/yellow]"
|
|
461
|
+
):
|
|
462
|
+
self.console.print("[yellow]Reset cancelled.[/yellow]")
|
|
463
|
+
Prompt.ask("Press Enter to continue")
|
|
464
|
+
return
|
|
465
|
+
|
|
466
|
+
# Enable the agent
|
|
467
|
+
self.agent_manager.set_agent_enabled(agent.name, True)
|
|
468
|
+
|
|
469
|
+
# Remove custom template if exists
|
|
470
|
+
template_path = self.get_agent_template_path(agent.name)
|
|
471
|
+
if template_path.exists() and not str(template_path).startswith(
|
|
472
|
+
str(self.agent_manager.templates_dir)
|
|
473
|
+
):
|
|
474
|
+
# This is a custom template, remove it
|
|
475
|
+
template_path.unlink(missing_ok=True)
|
|
476
|
+
self.console.print(
|
|
477
|
+
f"[green]✓ Removed custom template for '{agent.name}'[/green]"
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
self.console.print(
|
|
481
|
+
f"[green]✓ Agent '{agent.name}' reset to defaults![/green]"
|
|
482
|
+
)
|
|
483
|
+
self.console.print(
|
|
484
|
+
"[dim]Agent is now enabled with system template.[/dim]"
|
|
485
|
+
)
|
|
486
|
+
else:
|
|
487
|
+
self.console.print("[red]Invalid agent ID.[/red]")
|
|
488
|
+
|
|
489
|
+
except ValueError:
|
|
490
|
+
self.console.print("[red]Invalid input. Please enter a number.[/red]")
|
|
491
|
+
|
|
492
|
+
Prompt.ask("Press Enter to continue")
|
|
493
|
+
|
|
494
|
+
def edit_templates_interface(self) -> None:
|
|
495
|
+
"""Template editing interface (stub for future expansion)."""
|
|
496
|
+
self.console.print("[yellow]Template editing interface - Coming soon![/yellow]")
|
|
497
|
+
Prompt.ask("Press Enter to continue")
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Validation utilities for configure command.
|
|
2
|
+
|
|
3
|
+
WHY: Centralizes argument validation and input parsing logic.
|
|
4
|
+
Separates validation concerns from business logic.
|
|
5
|
+
|
|
6
|
+
DESIGN: Pure validation functions without side effects.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from argparse import Namespace
|
|
10
|
+
from typing import List, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def validate_args(args: Namespace) -> Optional[str]:
|
|
14
|
+
"""Validate command arguments.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
args: Parsed command-line arguments
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Error message if validation fails, None otherwise
|
|
21
|
+
"""
|
|
22
|
+
# Check for conflicting direct navigation options
|
|
23
|
+
nav_options = [
|
|
24
|
+
getattr(args, "agents", False),
|
|
25
|
+
getattr(args, "templates", False),
|
|
26
|
+
getattr(args, "behaviors", False),
|
|
27
|
+
getattr(args, "startup", False),
|
|
28
|
+
getattr(args, "version_info", False),
|
|
29
|
+
]
|
|
30
|
+
if sum(nav_options) > 1:
|
|
31
|
+
return "Only one direct navigation option can be specified at a time"
|
|
32
|
+
|
|
33
|
+
# Check for conflicting non-interactive options
|
|
34
|
+
if getattr(args, "enable_agent", None) and getattr(args, "disable_agent", None):
|
|
35
|
+
return "Cannot enable and disable agents at the same time"
|
|
36
|
+
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def parse_id_selection(selection: str, max_id: int) -> List[int]:
|
|
41
|
+
"""Parse ID selection string (e.g., '1,3,5' or '1-4').
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
selection: User selection string
|
|
45
|
+
max_id: Maximum valid ID
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
List of selected IDs (sorted)
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
ValueError: If selection is invalid
|
|
52
|
+
"""
|
|
53
|
+
ids = set()
|
|
54
|
+
parts = selection.split(",")
|
|
55
|
+
|
|
56
|
+
for part in parts:
|
|
57
|
+
part = part.strip()
|
|
58
|
+
if "-" in part:
|
|
59
|
+
# Range selection
|
|
60
|
+
start, end = part.split("-")
|
|
61
|
+
start_id = int(start.strip())
|
|
62
|
+
end_id = int(end.strip())
|
|
63
|
+
if start_id < 1 or end_id > max_id or start_id > end_id:
|
|
64
|
+
raise ValueError(f"Invalid range: {part}")
|
|
65
|
+
ids.update(range(start_id, end_id + 1))
|
|
66
|
+
else:
|
|
67
|
+
# Single ID
|
|
68
|
+
id_num = int(part)
|
|
69
|
+
if id_num < 1 or id_num > max_id:
|
|
70
|
+
raise ValueError(f"Invalid ID: {id_num}")
|
|
71
|
+
ids.add(id_num)
|
|
72
|
+
|
|
73
|
+
return sorted(ids)
|