claude-mpm 3.4.27__py3-none-any.whl → 3.5.0__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 (123) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/INSTRUCTIONS.md +182 -299
  3. claude_mpm/agents/agent_loader.py +283 -57
  4. claude_mpm/agents/agent_loader_integration.py +6 -9
  5. claude_mpm/agents/base_agent.json +2 -1
  6. claude_mpm/agents/base_agent_loader.py +1 -1
  7. claude_mpm/cli/__init__.py +5 -7
  8. claude_mpm/cli/commands/__init__.py +0 -2
  9. claude_mpm/cli/commands/agents.py +1 -1
  10. claude_mpm/cli/commands/memory.py +1 -1
  11. claude_mpm/cli/commands/run.py +12 -0
  12. claude_mpm/cli/parser.py +0 -13
  13. claude_mpm/cli/utils.py +1 -1
  14. claude_mpm/config/__init__.py +44 -2
  15. claude_mpm/config/agent_config.py +348 -0
  16. claude_mpm/config/paths.py +322 -0
  17. claude_mpm/constants.py +0 -1
  18. claude_mpm/core/__init__.py +2 -5
  19. claude_mpm/core/agent_registry.py +63 -17
  20. claude_mpm/core/claude_runner.py +354 -43
  21. claude_mpm/core/config.py +7 -1
  22. claude_mpm/core/config_aliases.py +4 -3
  23. claude_mpm/core/config_paths.py +151 -0
  24. claude_mpm/core/factories.py +4 -50
  25. claude_mpm/core/logger.py +11 -13
  26. claude_mpm/core/service_registry.py +2 -2
  27. claude_mpm/dashboard/static/js/components/agent-inference.js +101 -25
  28. claude_mpm/dashboard/static/js/components/event-processor.js +3 -2
  29. claude_mpm/hooks/claude_hooks/hook_handler.py +343 -83
  30. claude_mpm/hooks/memory_integration_hook.py +1 -1
  31. claude_mpm/init.py +37 -6
  32. claude_mpm/scripts/socketio_daemon.py +6 -2
  33. claude_mpm/services/__init__.py +71 -3
  34. claude_mpm/services/agents/__init__.py +85 -0
  35. claude_mpm/services/agents/deployment/__init__.py +21 -0
  36. claude_mpm/services/{agent_deployment.py → agents/deployment/agent_deployment.py} +192 -41
  37. claude_mpm/services/{agent_lifecycle_manager.py → agents/deployment/agent_lifecycle_manager.py} +11 -10
  38. claude_mpm/services/agents/loading/__init__.py +11 -0
  39. claude_mpm/services/{agent_profile_loader.py → agents/loading/agent_profile_loader.py} +9 -8
  40. claude_mpm/services/{base_agent_manager.py → agents/loading/base_agent_manager.py} +2 -2
  41. claude_mpm/services/{framework_agent_loader.py → agents/loading/framework_agent_loader.py} +116 -40
  42. claude_mpm/services/agents/management/__init__.py +9 -0
  43. claude_mpm/services/{agent_management_service.py → agents/management/agent_management_service.py} +6 -5
  44. claude_mpm/services/agents/memory/__init__.py +21 -0
  45. claude_mpm/services/{agent_memory_manager.py → agents/memory/agent_memory_manager.py} +3 -3
  46. claude_mpm/services/agents/registry/__init__.py +29 -0
  47. claude_mpm/services/{agent_registry.py → agents/registry/agent_registry.py} +101 -16
  48. claude_mpm/services/{deployed_agent_discovery.py → agents/registry/deployed_agent_discovery.py} +12 -2
  49. claude_mpm/services/{agent_modification_tracker.py → agents/registry/modification_tracker.py} +6 -5
  50. claude_mpm/services/async_session_logger.py +584 -0
  51. claude_mpm/services/claude_session_logger.py +299 -0
  52. claude_mpm/services/framework_claude_md_generator/content_assembler.py +2 -2
  53. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +17 -17
  54. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +3 -3
  55. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +1 -1
  56. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +1 -1
  57. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +19 -24
  58. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +1 -1
  59. claude_mpm/services/framework_claude_md_generator.py +4 -2
  60. claude_mpm/services/memory/__init__.py +17 -0
  61. claude_mpm/services/{memory_builder.py → memory/builder.py} +3 -3
  62. claude_mpm/services/memory/cache/__init__.py +14 -0
  63. claude_mpm/services/{shared_prompt_cache.py → memory/cache/shared_prompt_cache.py} +1 -1
  64. claude_mpm/services/memory/cache/simple_cache.py +317 -0
  65. claude_mpm/services/{memory_optimizer.py → memory/optimizer.py} +1 -1
  66. claude_mpm/services/{memory_router.py → memory/router.py} +1 -1
  67. claude_mpm/services/optimized_hook_service.py +542 -0
  68. claude_mpm/services/project_registry.py +14 -8
  69. claude_mpm/services/response_tracker.py +237 -0
  70. claude_mpm/services/ticketing_service_original.py +4 -2
  71. claude_mpm/services/version_control/branch_strategy.py +3 -1
  72. claude_mpm/utils/paths.py +12 -10
  73. claude_mpm/utils/session_logging.py +114 -0
  74. claude_mpm/validation/agent_validator.py +2 -1
  75. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/METADATA +26 -20
  76. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/RECORD +83 -106
  77. claude_mpm/cli/commands/ui.py +0 -57
  78. claude_mpm/core/simple_runner.py +0 -1046
  79. claude_mpm/hooks/builtin/__init__.py +0 -1
  80. claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
  81. claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
  82. claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
  83. claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
  84. claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
  85. claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
  86. claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
  87. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
  88. claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
  89. claude_mpm/orchestration/__init__.py +0 -6
  90. claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
  91. claude_mpm/orchestration/archive/factory.py +0 -215
  92. claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
  93. claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
  94. claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
  95. claude_mpm/orchestration/archive/orchestrator.py +0 -501
  96. claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
  97. claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
  98. claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
  99. claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
  100. claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
  101. claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
  102. claude_mpm/schemas/workflow_validator.py +0 -411
  103. claude_mpm/services/parent_directory_manager/__init__.py +0 -577
  104. claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
  105. claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
  106. claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
  107. claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
  108. claude_mpm/services/parent_directory_manager/operations.py +0 -186
  109. claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
  110. claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
  111. claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
  112. claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
  113. claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
  114. claude_mpm/ui/__init__.py +0 -1
  115. claude_mpm/ui/rich_terminal_ui.py +0 -295
  116. claude_mpm/ui/terminal_ui.py +0 -328
  117. /claude_mpm/services/{agent_versioning.py → agents/deployment/agent_versioning.py} +0 -0
  118. /claude_mpm/services/{agent_capabilities_generator.py → agents/management/agent_capabilities_generator.py} +0 -0
  119. /claude_mpm/services/{agent_persistence_service.py → agents/memory/agent_persistence_service.py} +0 -0
  120. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/WHEEL +0 -0
  121. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/entry_points.txt +0 -0
  122. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/licenses/LICENSE +0 -0
  123. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/top_level.txt +0 -0
@@ -1,258 +0,0 @@
1
- """Backup management functionality for Claude PM framework."""
2
-
3
- from pathlib import Path
4
- from typing import Optional, Dict, Any
5
- import datetime
6
- import shutil
7
- import logging
8
-
9
- # Import needed for restore operation
10
- from .state_manager import ParentDirectoryOperation, ParentDirectoryAction
11
- from ...utils.path_operations import path_ops
12
-
13
-
14
- class BackupManager:
15
- """Manages backup operations for files and framework templates."""
16
-
17
- def __init__(self, base_dir: Path, retention_days: int = 30, logger: Optional[logging.Logger] = None):
18
- """Initialize BackupManager.
19
-
20
- Args:
21
- base_dir: Base directory for the service (typically .claude-pm)
22
- retention_days: Number of days to retain backups (default: 30)
23
- logger: Logger instance for logging operations
24
- """
25
- self.base_dir = base_dir
26
- self.retention_days = retention_days
27
- self.logger = logger or logging.getLogger(__name__)
28
-
29
- # Ensure framework backup directory exists
30
- self.framework_backups_dir = self.base_dir / "backups" / "framework"
31
- path_ops.ensure_dir(self.framework_backups_dir)
32
-
33
- def create_backup(self, file_path: Path, backups_dir: Path) -> Optional[Path]:
34
- """Create a timestamped backup of a file.
35
-
36
- Args:
37
- file_path: Path to the file to backup
38
- backups_dir: Directory to store the backup
39
-
40
- Returns:
41
- Path to the created backup file, or None if backup failed
42
- """
43
- if not path_ops.validate_exists(file_path):
44
- self.logger.warning(f"Cannot backup non-existent file: {file_path}")
45
- return None
46
-
47
- try:
48
- # Create backups directory if it doesn't exist
49
- path_ops.ensure_dir(backups_dir)
50
-
51
- # Generate timestamp for backup filename
52
- timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f")[:-3]
53
- backup_filename = f"{file_path.stem}_{timestamp}.backup"
54
- backup_path = backups_dir / backup_filename
55
-
56
- # Copy file to backup location
57
- path_ops.safe_copy(file_path, backup_path)
58
- self.logger.info(f"Created backup: {backup_path}")
59
-
60
- return backup_path
61
-
62
- except Exception as e:
63
- self.logger.error(f"Failed to create backup of {file_path}: {e}")
64
- return None
65
-
66
- async def backup_parent_directory(self, target_directory: Path, backups_dir: Path) -> Optional[Path]:
67
- """
68
- Create a backup of a parent directory's INSTRUCTIONS.md/CLAUDE.md file.
69
-
70
- Args:
71
- target_directory: Directory containing file to backup
72
- backups_dir: Directory to store backups
73
-
74
- Returns:
75
- Path to backup file or None if failed
76
- """
77
- try:
78
- # Check for INSTRUCTIONS.md first, then CLAUDE.md
79
- target_file = target_directory / "INSTRUCTIONS.md"
80
- if not path_ops.validate_exists(target_file):
81
- target_file = target_directory / "CLAUDE.md"
82
-
83
- if not path_ops.validate_exists(target_file):
84
- self.logger.warning(f"No INSTRUCTIONS.md or CLAUDE.md file to backup in {target_directory}")
85
- return None
86
-
87
- # Delegate to create_backup
88
- return self.create_backup(target_file, backups_dir)
89
-
90
- except Exception as e:
91
- self.logger.error(f"Failed to backup parent directory {target_directory}: {e}")
92
- return None
93
-
94
- def backup_framework_template(self, framework_template_path: Path) -> Optional[Path]:
95
- """Create a backup of the framework template with rotation.
96
-
97
- Only keeps the 2 most recent backups to prevent accumulation.
98
-
99
- Args:
100
- framework_template_path: Path to the framework template file
101
-
102
- Returns:
103
- Path to the created backup, or None if backup failed
104
- """
105
- if not path_ops.validate_exists(framework_template_path):
106
- self.logger.warning(f"Framework template not found: {framework_template_path}")
107
- return None
108
-
109
- try:
110
- # Create backup
111
- backup_path = self.create_backup(framework_template_path, self.framework_backups_dir)
112
-
113
- if backup_path:
114
- # Rotate backups to keep only 2 most recent
115
- self._rotate_framework_backups()
116
-
117
- return backup_path
118
-
119
- except Exception as e:
120
- self.logger.error(f"Failed to backup framework template: {e}")
121
- return None
122
-
123
- def get_framework_backup_status(self) -> Dict[str, Any]:
124
- """Get status information about framework backups.
125
-
126
- Returns:
127
- Dictionary containing backup status information
128
- """
129
- try:
130
- backup_files = list(self.framework_backups_dir.glob("framework_CLAUDE_md_*.backup"))
131
- backup_files.sort(key=lambda p: p.stat().st_mtime, reverse=True)
132
-
133
- status = {
134
- "backup_dir": str(self.framework_backups_dir),
135
- "backup_count": len(backup_files),
136
- "backups": []
137
- }
138
-
139
- for backup_file in backup_files:
140
- try:
141
- stat = backup_file.stat()
142
- backup_info = {
143
- "filename": backup_file.name,
144
- "path": str(backup_file),
145
- "size": stat.st_size,
146
- "modified": datetime.datetime.fromtimestamp(stat.st_mtime).isoformat(),
147
- "age_days": (datetime.datetime.now() - datetime.datetime.fromtimestamp(stat.st_mtime)).days
148
- }
149
- status["backups"].append(backup_info)
150
- except Exception as e:
151
- self.logger.warning(f"Could not get info for backup {backup_file}: {e}")
152
-
153
- return status
154
-
155
- except Exception as e:
156
- self.logger.error(f"Failed to get framework backup status: {e}")
157
- return {
158
- "backup_dir": str(self.framework_backups_dir),
159
- "backup_count": 0,
160
- "backups": [],
161
- "error": str(e)
162
- }
163
-
164
- def _rotate_framework_backups(self) -> None:
165
- """Rotate framework backups to keep only the 2 most recent."""
166
- try:
167
- # Get all framework backup files
168
- backup_files = list(self.framework_backups_dir.glob("framework_CLAUDE_md_*.backup"))
169
-
170
- if len(backup_files) <= 2:
171
- return
172
-
173
- # Sort by modification time (newest first)
174
- backup_files.sort(key=lambda p: p.stat().st_mtime, reverse=True)
175
-
176
- # Remove all but the 2 most recent
177
- for old_backup in backup_files[2:]:
178
- try:
179
- old_backup.unlink()
180
- self.logger.info(f"Removed old framework backup: {old_backup.name}")
181
- except Exception as e:
182
- self.logger.warning(f"Could not remove old backup {old_backup}: {e}")
183
-
184
- except Exception as e:
185
- self.logger.error(f"Failed to rotate framework backups: {e}")
186
-
187
- async def restore_from_backup(
188
- self,
189
- target_directory: Path,
190
- backups_dir: Path,
191
- backup_timestamp: Optional[str] = None
192
- ) -> ParentDirectoryOperation:
193
- """
194
- Restore a parent directory from backup.
195
-
196
- Args:
197
- target_directory: Directory to restore
198
- backups_dir: Directory containing backups
199
- backup_timestamp: Specific backup to restore (latest if None)
200
-
201
- Returns:
202
- ParentDirectoryOperation result
203
- """
204
- try:
205
- # Check for INSTRUCTIONS.md first, then CLAUDE.md
206
- target_file = target_directory / "INSTRUCTIONS.md"
207
- if not target_file.exists():
208
- target_file = target_directory / "CLAUDE.md"
209
-
210
- # Find backup files
211
- backup_pattern = f"*{target_file.name}*"
212
- backup_files = list(backups_dir.glob(backup_pattern))
213
-
214
- if not backup_files:
215
- raise ValueError(f"No backup files found for {target_file}")
216
-
217
- # Select backup file
218
- if backup_timestamp:
219
- backup_file = None
220
- for bf in backup_files:
221
- if backup_timestamp in bf.name:
222
- backup_file = bf
223
- break
224
-
225
- if not backup_file:
226
- raise ValueError(f"No backup found for timestamp: {backup_timestamp}")
227
- else:
228
- # Use most recent backup
229
- backup_file = max(backup_files, key=lambda f: f.stat().st_mtime)
230
-
231
- # Create backup of current file if it exists
232
- current_backup = None
233
- if target_file.exists():
234
- current_backup = self.create_backup(target_file, backups_dir)
235
-
236
- # Restore from backup
237
- shutil.copy2(backup_file, target_file)
238
-
239
- # Create operation result
240
- operation = ParentDirectoryOperation(
241
- action=ParentDirectoryAction.RESTORE,
242
- target_path=target_file,
243
- success=True,
244
- backup_path=current_backup,
245
- changes_made=[f"Restored {target_file} from backup {backup_file}"],
246
- )
247
-
248
- self.logger.info(f"Successfully restored {target_file} from backup")
249
- return operation
250
-
251
- except Exception as e:
252
- self.logger.error(f"Failed to restore parent directory {target_directory}: {e}")
253
- return ParentDirectoryOperation(
254
- action=ParentDirectoryAction.RESTORE,
255
- target_path=target_file,
256
- success=False,
257
- error_message=str(e),
258
- )
@@ -1,210 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Configuration Manager - Handles managed directories configuration
4
- ================================================================================
5
-
6
- This module manages the loading, saving, and tracking of managed directory
7
- configurations for the parent directory manager.
8
- """
9
-
10
- from pathlib import Path
11
- from typing import Dict, Any, Optional
12
- from dataclasses import dataclass, field
13
- import logging
14
-
15
- from .operations import ParentDirectoryContext
16
- from ...utils.path_operations import path_ops
17
- from ...utils.config_manager import ConfigurationManager
18
-
19
-
20
- @dataclass
21
- class ParentDirectoryConfig:
22
- """Configuration for parent directory management."""
23
- target_directory: Path
24
- context: ParentDirectoryContext
25
- template_id: str
26
- template_variables: Dict[str, Any] = field(default_factory=dict)
27
- backup_enabled: bool = True
28
- version_control: bool = True
29
- conflict_resolution: str = "backup_and_replace"
30
- deployment_metadata: Dict[str, Any] = field(default_factory=dict)
31
-
32
-
33
- class ConfigManager:
34
- """Manages configuration for parent directories."""
35
-
36
- def __init__(
37
- self,
38
- configs_dir: Path,
39
- logger: Optional[logging.Logger] = None
40
- ):
41
- """
42
- Initialize the Configuration Manager.
43
-
44
- Args:
45
- configs_dir: Directory for storing configurations
46
- logger: Logger instance to use
47
- """
48
- self.configs_dir = configs_dir
49
- self.logger = logger or logging.getLogger(__name__)
50
- self.managed_directories_file = self.configs_dir / "managed_directories.json"
51
- self.managed_directories: Dict[str, ParentDirectoryConfig] = {}
52
- self.config_mgr = ConfigurationManager(cache_enabled=True)
53
-
54
- async def load_managed_directories(self) -> None:
55
- """Load existing managed directories configuration."""
56
- try:
57
- if path_ops.validate_exists(self.managed_directories_file):
58
- data = self.config_mgr.load_json(self.managed_directories_file)
59
-
60
- # Convert loaded data to ParentDirectoryConfig objects
61
- for key, config_data in data.items():
62
- config = ParentDirectoryConfig(
63
- target_directory=Path(config_data["target_directory"]),
64
- context=ParentDirectoryContext(config_data["context"]),
65
- template_id=config_data["template_id"],
66
- template_variables=config_data.get("template_variables", {}),
67
- backup_enabled=config_data.get("backup_enabled", True),
68
- version_control=config_data.get("version_control", True),
69
- conflict_resolution=config_data.get(
70
- "conflict_resolution", "backup_and_replace"
71
- ),
72
- deployment_metadata=config_data.get("deployment_metadata", {}),
73
- )
74
- self.managed_directories[key] = config
75
-
76
- self.logger.info(f"Loading managed directories...")
77
-
78
- except Exception as e:
79
- self.logger.error(f"Failed to load managed directories: {e}")
80
-
81
- async def save_managed_directories(self) -> None:
82
- """Save managed directories configuration."""
83
- try:
84
- # Convert ParentDirectoryConfig objects to serializable format
85
- data = {}
86
- for key, config in self.managed_directories.items():
87
- data[key] = {
88
- "target_directory": str(config.target_directory),
89
- "context": config.context.value,
90
- "template_id": config.template_id,
91
- "template_variables": config.template_variables,
92
- "backup_enabled": config.backup_enabled,
93
- "version_control": config.version_control,
94
- "conflict_resolution": config.conflict_resolution,
95
- "deployment_metadata": config.deployment_metadata,
96
- }
97
-
98
- self.config_mgr.save_json(data, self.managed_directories_file)
99
-
100
- self.logger.debug("Managed directories configuration saved")
101
-
102
- except Exception as e:
103
- self.logger.error(f"Failed to save managed directories: {e}")
104
-
105
- def register_directory(
106
- self,
107
- directory_key: str,
108
- config: ParentDirectoryConfig
109
- ) -> None:
110
- """
111
- Register a directory configuration.
112
-
113
- Args:
114
- directory_key: Unique key for the directory
115
- config: Configuration for the directory
116
- """
117
- self.managed_directories[directory_key] = config
118
-
119
- async def register_parent_directory(
120
- self,
121
- target_directory: Path,
122
- context: ParentDirectoryContext,
123
- template_id: str,
124
- template_variables: Dict[str, Any] = None,
125
- **kwargs
126
- ) -> bool:
127
- """
128
- Register a parent directory for management with validation.
129
-
130
- Args:
131
- target_directory: Directory to manage
132
- context: Context type for the directory
133
- template_id: Template to use for management
134
- template_variables: Variables for template rendering
135
- **kwargs: Additional configuration options
136
-
137
- Returns:
138
- True if registration successful, False otherwise
139
- """
140
- try:
141
- # Validate inputs
142
- if not path_ops.validate_exists(target_directory):
143
- raise ValueError(f"Target directory does not exist: {target_directory}")
144
-
145
- if not path_ops.validate_is_dir(target_directory):
146
- raise ValueError(f"Target path is not a directory: {target_directory}")
147
-
148
- # Create configuration
149
- config = ParentDirectoryConfig(
150
- target_directory=target_directory,
151
- context=context,
152
- template_id=template_id,
153
- template_variables=template_variables or {},
154
- backup_enabled=kwargs.get("backup_enabled", True),
155
- version_control=kwargs.get("version_control", True),
156
- conflict_resolution=kwargs.get("conflict_resolution", "backup_and_replace"),
157
- deployment_metadata=kwargs.get("deployment_metadata", {}),
158
- )
159
-
160
- # Register the directory
161
- directory_key = str(target_directory)
162
- self.register_directory(directory_key, config)
163
-
164
- # Save configuration
165
- await self.save_managed_directories()
166
-
167
- self.logger.info(
168
- f"Registered parent directory: {target_directory} with template {template_id}"
169
- )
170
- return True
171
-
172
- except Exception as e:
173
- self.logger.error(f"Failed to register parent directory {target_directory}: {e}")
174
- return False
175
-
176
- def get_directory_config(
177
- self,
178
- directory_key: str
179
- ) -> Optional[ParentDirectoryConfig]:
180
- """
181
- Get configuration for a directory.
182
-
183
- Args:
184
- directory_key: Unique key for the directory
185
-
186
- Returns:
187
- Directory configuration or None if not found
188
- """
189
- return self.managed_directories.get(directory_key)
190
-
191
- def is_directory_managed(self, directory_key: str) -> bool:
192
- """
193
- Check if a directory is managed.
194
-
195
- Args:
196
- directory_key: Unique key for the directory
197
-
198
- Returns:
199
- True if directory is managed, False otherwise
200
- """
201
- return directory_key in self.managed_directories
202
-
203
- def get_all_managed_directories(self) -> Dict[str, ParentDirectoryConfig]:
204
- """
205
- Get all managed directory configurations.
206
-
207
- Returns:
208
- Dictionary of all managed directories
209
- """
210
- return self.managed_directories.copy()