claude-mpm 4.7.4__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.
Files changed (308) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +118 -0
  3. claude_mpm/agents/BASE_ENGINEER.md +286 -0
  4. claude_mpm/agents/BASE_PM.md +106 -1
  5. claude_mpm/agents/OUTPUT_STYLE.md +329 -11
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +397 -459
  7. claude_mpm/agents/agent_loader.py +17 -5
  8. claude_mpm/agents/frontmatter_validator.py +284 -253
  9. claude_mpm/agents/templates/README.md +465 -0
  10. claude_mpm/agents/templates/agent-manager.json +4 -1
  11. claude_mpm/agents/templates/agentic-coder-optimizer.json +13 -3
  12. claude_mpm/agents/templates/api_qa.json +11 -2
  13. claude_mpm/agents/templates/circuit_breakers.md +638 -0
  14. claude_mpm/agents/templates/clerk-ops.json +12 -2
  15. claude_mpm/agents/templates/code_analyzer.json +8 -2
  16. claude_mpm/agents/templates/content-agent.json +358 -0
  17. claude_mpm/agents/templates/dart_engineer.json +15 -2
  18. claude_mpm/agents/templates/data_engineer.json +15 -2
  19. claude_mpm/agents/templates/documentation.json +10 -2
  20. claude_mpm/agents/templates/engineer.json +21 -1
  21. claude_mpm/agents/templates/gcp_ops_agent.json +12 -2
  22. claude_mpm/agents/templates/git_file_tracking.md +584 -0
  23. claude_mpm/agents/templates/golang_engineer.json +270 -0
  24. claude_mpm/agents/templates/imagemagick.json +4 -1
  25. claude_mpm/agents/templates/java_engineer.json +346 -0
  26. claude_mpm/agents/templates/local_ops_agent.json +1227 -6
  27. claude_mpm/agents/templates/memory_manager.json +4 -1
  28. claude_mpm/agents/templates/nextjs_engineer.json +141 -133
  29. claude_mpm/agents/templates/ops.json +12 -2
  30. claude_mpm/agents/templates/php-engineer.json +270 -174
  31. claude_mpm/agents/templates/pm_examples.md +474 -0
  32. claude_mpm/agents/templates/pm_red_flags.md +240 -0
  33. claude_mpm/agents/templates/product_owner.json +338 -0
  34. claude_mpm/agents/templates/project_organizer.json +14 -4
  35. claude_mpm/agents/templates/prompt-engineer.json +13 -2
  36. claude_mpm/agents/templates/python_engineer.json +174 -81
  37. claude_mpm/agents/templates/qa.json +11 -2
  38. claude_mpm/agents/templates/react_engineer.json +16 -3
  39. claude_mpm/agents/templates/refactoring_engineer.json +12 -2
  40. claude_mpm/agents/templates/research.json +34 -21
  41. claude_mpm/agents/templates/response_format.md +583 -0
  42. claude_mpm/agents/templates/ruby-engineer.json +129 -192
  43. claude_mpm/agents/templates/rust_engineer.json +270 -0
  44. claude_mpm/agents/templates/security.json +10 -2
  45. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  46. claude_mpm/agents/templates/ticketing.json +10 -2
  47. claude_mpm/agents/templates/typescript_engineer.json +116 -125
  48. claude_mpm/agents/templates/validation_templates.md +312 -0
  49. claude_mpm/agents/templates/vercel_ops_agent.json +12 -2
  50. claude_mpm/agents/templates/version_control.json +12 -2
  51. claude_mpm/agents/templates/web_qa.json +11 -2
  52. claude_mpm/agents/templates/web_ui.json +15 -2
  53. claude_mpm/cli/__init__.py +34 -614
  54. claude_mpm/cli/commands/agent_manager.py +25 -12
  55. claude_mpm/cli/commands/agent_state_manager.py +186 -0
  56. claude_mpm/cli/commands/agents.py +235 -148
  57. claude_mpm/cli/commands/agents_detect.py +380 -0
  58. claude_mpm/cli/commands/agents_recommend.py +309 -0
  59. claude_mpm/cli/commands/aggregate.py +7 -3
  60. claude_mpm/cli/commands/analyze.py +9 -4
  61. claude_mpm/cli/commands/analyze_code.py +7 -2
  62. claude_mpm/cli/commands/auto_configure.py +570 -0
  63. claude_mpm/cli/commands/config.py +47 -13
  64. claude_mpm/cli/commands/configure.py +419 -1571
  65. claude_mpm/cli/commands/configure_agent_display.py +261 -0
  66. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  67. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  68. claude_mpm/cli/commands/configure_models.py +18 -0
  69. claude_mpm/cli/commands/configure_navigation.py +167 -0
  70. claude_mpm/cli/commands/configure_paths.py +104 -0
  71. claude_mpm/cli/commands/configure_persistence.py +254 -0
  72. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  73. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  74. claude_mpm/cli/commands/configure_validators.py +73 -0
  75. claude_mpm/cli/commands/local_deploy.py +537 -0
  76. claude_mpm/cli/commands/memory.py +54 -20
  77. claude_mpm/cli/commands/mpm_init.py +585 -196
  78. claude_mpm/cli/commands/mpm_init_handler.py +37 -3
  79. claude_mpm/cli/commands/search.py +170 -4
  80. claude_mpm/cli/commands/upgrade.py +152 -0
  81. claude_mpm/cli/executor.py +202 -0
  82. claude_mpm/cli/helpers.py +105 -0
  83. claude_mpm/cli/interactive/__init__.py +3 -0
  84. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  85. claude_mpm/cli/parsers/__init__.py +7 -1
  86. claude_mpm/cli/parsers/agents_parser.py +9 -0
  87. claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
  88. claude_mpm/cli/parsers/base_parser.py +110 -3
  89. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  90. claude_mpm/cli/parsers/mpm_init_parser.py +65 -5
  91. claude_mpm/cli/shared/output_formatters.py +28 -19
  92. claude_mpm/cli/startup.py +481 -0
  93. claude_mpm/cli/utils.py +52 -1
  94. claude_mpm/commands/mpm-agents-detect.md +168 -0
  95. claude_mpm/commands/mpm-agents-recommend.md +214 -0
  96. claude_mpm/commands/mpm-agents.md +75 -1
  97. claude_mpm/commands/mpm-auto-configure.md +217 -0
  98. claude_mpm/commands/mpm-help.md +163 -0
  99. claude_mpm/commands/mpm-init.md +148 -3
  100. claude_mpm/commands/mpm-version.md +113 -0
  101. claude_mpm/commands/mpm.md +1 -0
  102. claude_mpm/config/agent_config.py +2 -2
  103. claude_mpm/config/model_config.py +428 -0
  104. claude_mpm/constants.py +1 -0
  105. claude_mpm/core/base_service.py +13 -12
  106. claude_mpm/core/enums.py +452 -0
  107. claude_mpm/core/factories.py +1 -1
  108. claude_mpm/core/instruction_reinforcement_hook.py +2 -1
  109. claude_mpm/core/interactive_session.py +9 -3
  110. claude_mpm/core/log_manager.py +2 -0
  111. claude_mpm/core/logging_config.py +6 -2
  112. claude_mpm/core/oneshot_session.py +8 -4
  113. claude_mpm/core/optimized_agent_loader.py +3 -3
  114. claude_mpm/core/output_style_manager.py +12 -192
  115. claude_mpm/core/service_registry.py +5 -1
  116. claude_mpm/core/types.py +2 -9
  117. claude_mpm/core/typing_utils.py +7 -6
  118. claude_mpm/dashboard/static/js/dashboard.js +0 -14
  119. claude_mpm/dashboard/templates/index.html +3 -41
  120. claude_mpm/hooks/__init__.py +20 -0
  121. claude_mpm/hooks/claude_hooks/event_handlers.py +4 -2
  122. claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
  123. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +23 -2
  124. claude_mpm/hooks/failure_learning/__init__.py +60 -0
  125. claude_mpm/hooks/failure_learning/failure_detection_hook.py +235 -0
  126. claude_mpm/hooks/failure_learning/fix_detection_hook.py +217 -0
  127. claude_mpm/hooks/failure_learning/learning_extraction_hook.py +286 -0
  128. claude_mpm/hooks/instruction_reinforcement.py +7 -2
  129. claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
  130. claude_mpm/hooks/kuzu_memory_hook.py +37 -12
  131. claude_mpm/hooks/kuzu_response_hook.py +183 -0
  132. claude_mpm/models/resume_log.py +340 -0
  133. claude_mpm/services/agents/__init__.py +18 -5
  134. claude_mpm/services/agents/auto_config_manager.py +796 -0
  135. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  136. claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
  137. claude_mpm/services/agents/deployment/agent_validator.py +17 -1
  138. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  139. claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
  140. claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
  141. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
  142. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  143. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  144. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
  145. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  146. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
  147. claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
  148. claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
  149. claude_mpm/services/agents/local_template_manager.py +1 -1
  150. claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
  151. claude_mpm/services/agents/observers.py +547 -0
  152. claude_mpm/services/agents/recommender.py +568 -0
  153. claude_mpm/services/agents/registry/modification_tracker.py +5 -2
  154. claude_mpm/services/command_handler_service.py +11 -5
  155. claude_mpm/services/core/__init__.py +33 -1
  156. claude_mpm/services/core/interfaces/__init__.py +90 -3
  157. claude_mpm/services/core/interfaces/agent.py +184 -0
  158. claude_mpm/services/core/interfaces/health.py +172 -0
  159. claude_mpm/services/core/interfaces/model.py +281 -0
  160. claude_mpm/services/core/interfaces/process.py +372 -0
  161. claude_mpm/services/core/interfaces/project.py +121 -0
  162. claude_mpm/services/core/interfaces/restart.py +307 -0
  163. claude_mpm/services/core/interfaces/stability.py +260 -0
  164. claude_mpm/services/core/memory_manager.py +11 -24
  165. claude_mpm/services/core/models/__init__.py +79 -0
  166. claude_mpm/services/core/models/agent_config.py +381 -0
  167. claude_mpm/services/core/models/health.py +162 -0
  168. claude_mpm/services/core/models/process.py +235 -0
  169. claude_mpm/services/core/models/restart.py +302 -0
  170. claude_mpm/services/core/models/stability.py +264 -0
  171. claude_mpm/services/core/models/toolchain.py +306 -0
  172. claude_mpm/services/core/path_resolver.py +23 -7
  173. claude_mpm/services/diagnostics/__init__.py +2 -2
  174. claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
  175. claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
  176. claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
  177. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
  178. claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
  179. claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
  180. claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
  181. claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
  182. claude_mpm/services/diagnostics/checks/mcp_services_check.py +38 -33
  183. claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
  184. claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
  185. claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
  186. claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
  187. claude_mpm/services/diagnostics/models.py +19 -24
  188. claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
  189. claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
  190. claude_mpm/services/infrastructure/monitoring/base.py +5 -13
  191. claude_mpm/services/infrastructure/monitoring/network.py +7 -6
  192. claude_mpm/services/infrastructure/monitoring/process.py +13 -12
  193. claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
  194. claude_mpm/services/infrastructure/monitoring/service.py +16 -15
  195. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  196. claude_mpm/services/local_ops/__init__.py +163 -0
  197. claude_mpm/services/local_ops/crash_detector.py +257 -0
  198. claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
  199. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  200. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  201. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  202. claude_mpm/services/local_ops/health_manager.py +430 -0
  203. claude_mpm/services/local_ops/log_monitor.py +396 -0
  204. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  205. claude_mpm/services/local_ops/process_manager.py +595 -0
  206. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  207. claude_mpm/services/local_ops/restart_manager.py +401 -0
  208. claude_mpm/services/local_ops/restart_policy.py +387 -0
  209. claude_mpm/services/local_ops/state_manager.py +372 -0
  210. claude_mpm/services/local_ops/unified_manager.py +600 -0
  211. claude_mpm/services/mcp_config_manager.py +9 -4
  212. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  213. claude_mpm/services/mcp_gateway/core/base.py +18 -31
  214. claude_mpm/services/mcp_gateway/main.py +30 -0
  215. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +206 -32
  216. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
  217. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +25 -5
  218. claude_mpm/services/mcp_service_verifier.py +1 -1
  219. claude_mpm/services/memory/failure_tracker.py +563 -0
  220. claude_mpm/services/memory_hook_service.py +165 -4
  221. claude_mpm/services/model/__init__.py +147 -0
  222. claude_mpm/services/model/base_provider.py +365 -0
  223. claude_mpm/services/model/claude_provider.py +412 -0
  224. claude_mpm/services/model/model_router.py +453 -0
  225. claude_mpm/services/model/ollama_provider.py +415 -0
  226. claude_mpm/services/monitor/daemon_manager.py +3 -2
  227. claude_mpm/services/monitor/handlers/dashboard.py +2 -1
  228. claude_mpm/services/monitor/handlers/hooks.py +2 -1
  229. claude_mpm/services/monitor/management/lifecycle.py +3 -2
  230. claude_mpm/services/monitor/server.py +2 -1
  231. claude_mpm/services/project/__init__.py +23 -0
  232. claude_mpm/services/project/detection_strategies.py +719 -0
  233. claude_mpm/services/project/toolchain_analyzer.py +581 -0
  234. claude_mpm/services/self_upgrade_service.py +342 -0
  235. claude_mpm/services/session_management_service.py +3 -2
  236. claude_mpm/services/session_manager.py +205 -1
  237. claude_mpm/services/shared/async_service_base.py +16 -27
  238. claude_mpm/services/shared/lifecycle_service_base.py +1 -14
  239. claude_mpm/services/socketio/handlers/__init__.py +5 -2
  240. claude_mpm/services/socketio/handlers/hook.py +13 -2
  241. claude_mpm/services/socketio/handlers/registry.py +4 -2
  242. claude_mpm/services/socketio/server/main.py +10 -8
  243. claude_mpm/services/subprocess_launcher_service.py +14 -5
  244. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
  245. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
  246. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
  247. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
  248. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
  249. claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
  250. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
  251. claude_mpm/services/unified/deployment_strategies/local.py +6 -5
  252. claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
  253. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
  254. claude_mpm/services/unified/interfaces.py +3 -1
  255. claude_mpm/services/unified/unified_analyzer.py +14 -10
  256. claude_mpm/services/unified/unified_config.py +2 -1
  257. claude_mpm/services/unified/unified_deployment.py +9 -4
  258. claude_mpm/services/version_service.py +104 -1
  259. claude_mpm/skills/__init__.py +21 -0
  260. claude_mpm/skills/bundled/__init__.py +6 -0
  261. claude_mpm/skills/bundled/api-documentation.md +393 -0
  262. claude_mpm/skills/bundled/async-testing.md +571 -0
  263. claude_mpm/skills/bundled/code-review.md +143 -0
  264. claude_mpm/skills/bundled/database-migration.md +199 -0
  265. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  266. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  267. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  268. claude_mpm/skills/bundled/git-workflow.md +414 -0
  269. claude_mpm/skills/bundled/imagemagick.md +204 -0
  270. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  271. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  272. claude_mpm/skills/bundled/pdf.md +141 -0
  273. claude_mpm/skills/bundled/performance-profiling.md +567 -0
  274. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  275. claude_mpm/skills/bundled/security-scanning.md +327 -0
  276. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  277. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  278. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  279. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  280. claude_mpm/skills/bundled/xlsx.md +157 -0
  281. claude_mpm/skills/registry.py +286 -0
  282. claude_mpm/skills/skill_manager.py +310 -0
  283. claude_mpm/storage/state_storage.py +15 -15
  284. claude_mpm/tools/code_tree_analyzer.py +177 -141
  285. claude_mpm/tools/code_tree_events.py +4 -2
  286. claude_mpm/utils/agent_dependency_loader.py +40 -20
  287. claude_mpm/utils/display_helper.py +260 -0
  288. claude_mpm/utils/git_analyzer.py +407 -0
  289. claude_mpm/utils/robust_installer.py +73 -19
  290. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +129 -12
  291. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +295 -193
  292. claude_mpm/dashboard/static/css/code-tree.css +0 -1639
  293. claude_mpm/dashboard/static/index-hub-backup.html +0 -713
  294. claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
  295. claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
  296. claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
  297. claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
  298. claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
  299. claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
  300. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
  301. claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
  302. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
  303. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
  304. claude_mpm/services/project/analyzer_refactored.py +0 -450
  305. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
  306. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
  307. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
  308. {claude_mpm-4.7.4.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)