claude-mpm 0.3.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.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (159) hide show
  1. claude_mpm/__init__.py +17 -0
  2. claude_mpm/__main__.py +14 -0
  3. claude_mpm/_version.py +32 -0
  4. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +88 -0
  5. claude_mpm/agents/INSTRUCTIONS.md +375 -0
  6. claude_mpm/agents/__init__.py +118 -0
  7. claude_mpm/agents/agent_loader.py +621 -0
  8. claude_mpm/agents/agent_loader_integration.py +229 -0
  9. claude_mpm/agents/agents_metadata.py +204 -0
  10. claude_mpm/agents/base_agent.json +27 -0
  11. claude_mpm/agents/base_agent_loader.py +519 -0
  12. claude_mpm/agents/schema/agent_schema.json +160 -0
  13. claude_mpm/agents/system_agent_config.py +587 -0
  14. claude_mpm/agents/templates/__init__.py +101 -0
  15. claude_mpm/agents/templates/data_engineer_agent.json +46 -0
  16. claude_mpm/agents/templates/documentation_agent.json +45 -0
  17. claude_mpm/agents/templates/engineer_agent.json +49 -0
  18. claude_mpm/agents/templates/ops_agent.json +46 -0
  19. claude_mpm/agents/templates/qa_agent.json +45 -0
  20. claude_mpm/agents/templates/research_agent.json +49 -0
  21. claude_mpm/agents/templates/security_agent.json +46 -0
  22. claude_mpm/agents/templates/update-optimized-specialized-agents.json +374 -0
  23. claude_mpm/agents/templates/version_control_agent.json +46 -0
  24. claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +6 -0
  25. claude_mpm/cli.py +655 -0
  26. claude_mpm/cli_main.py +13 -0
  27. claude_mpm/cli_module/__init__.py +15 -0
  28. claude_mpm/cli_module/args.py +222 -0
  29. claude_mpm/cli_module/commands.py +203 -0
  30. claude_mpm/cli_module/migration_example.py +183 -0
  31. claude_mpm/cli_module/refactoring_guide.md +253 -0
  32. claude_mpm/cli_old/__init__.py +1 -0
  33. claude_mpm/cli_old/ticket_cli.py +102 -0
  34. claude_mpm/config/__init__.py +5 -0
  35. claude_mpm/config/hook_config.py +42 -0
  36. claude_mpm/constants.py +150 -0
  37. claude_mpm/core/__init__.py +45 -0
  38. claude_mpm/core/agent_name_normalizer.py +248 -0
  39. claude_mpm/core/agent_registry.py +627 -0
  40. claude_mpm/core/agent_registry.py.bak +312 -0
  41. claude_mpm/core/agent_session_manager.py +273 -0
  42. claude_mpm/core/base_service.py +747 -0
  43. claude_mpm/core/base_service.py.bak +406 -0
  44. claude_mpm/core/config.py +334 -0
  45. claude_mpm/core/config_aliases.py +292 -0
  46. claude_mpm/core/container.py +347 -0
  47. claude_mpm/core/factories.py +281 -0
  48. claude_mpm/core/framework_loader.py +472 -0
  49. claude_mpm/core/injectable_service.py +206 -0
  50. claude_mpm/core/interfaces.py +539 -0
  51. claude_mpm/core/logger.py +468 -0
  52. claude_mpm/core/minimal_framework_loader.py +107 -0
  53. claude_mpm/core/mixins.py +150 -0
  54. claude_mpm/core/service_registry.py +299 -0
  55. claude_mpm/core/session_manager.py +190 -0
  56. claude_mpm/core/simple_runner.py +511 -0
  57. claude_mpm/core/tool_access_control.py +173 -0
  58. claude_mpm/hooks/README.md +243 -0
  59. claude_mpm/hooks/__init__.py +5 -0
  60. claude_mpm/hooks/base_hook.py +154 -0
  61. claude_mpm/hooks/builtin/__init__.py +1 -0
  62. claude_mpm/hooks/builtin/logging_hook_example.py +165 -0
  63. claude_mpm/hooks/builtin/post_delegation_hook_example.py +124 -0
  64. claude_mpm/hooks/builtin/pre_delegation_hook_example.py +125 -0
  65. claude_mpm/hooks/builtin/submit_hook_example.py +100 -0
  66. claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +237 -0
  67. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +239 -0
  68. claude_mpm/hooks/builtin/workflow_start_hook.py +181 -0
  69. claude_mpm/hooks/hook_client.py +264 -0
  70. claude_mpm/hooks/hook_runner.py +370 -0
  71. claude_mpm/hooks/json_rpc_executor.py +259 -0
  72. claude_mpm/hooks/json_rpc_hook_client.py +319 -0
  73. claude_mpm/hooks/tool_call_interceptor.py +204 -0
  74. claude_mpm/init.py +246 -0
  75. claude_mpm/orchestration/SUBPROCESS_DESIGN.md +66 -0
  76. claude_mpm/orchestration/__init__.py +6 -0
  77. claude_mpm/orchestration/archive/direct_orchestrator.py +195 -0
  78. claude_mpm/orchestration/archive/factory.py +215 -0
  79. claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +188 -0
  80. claude_mpm/orchestration/archive/hook_integration_example.py +178 -0
  81. claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +826 -0
  82. claude_mpm/orchestration/archive/orchestrator.py +501 -0
  83. claude_mpm/orchestration/archive/pexpect_orchestrator.py +252 -0
  84. claude_mpm/orchestration/archive/pty_orchestrator.py +270 -0
  85. claude_mpm/orchestration/archive/simple_orchestrator.py +82 -0
  86. claude_mpm/orchestration/archive/subprocess_orchestrator.py +801 -0
  87. claude_mpm/orchestration/archive/system_prompt_orchestrator.py +278 -0
  88. claude_mpm/orchestration/archive/wrapper_orchestrator.py +187 -0
  89. claude_mpm/scripts/__init__.py +1 -0
  90. claude_mpm/scripts/ticket.py +269 -0
  91. claude_mpm/services/__init__.py +10 -0
  92. claude_mpm/services/agent_deployment.py +955 -0
  93. claude_mpm/services/agent_lifecycle_manager.py +948 -0
  94. claude_mpm/services/agent_management_service.py +596 -0
  95. claude_mpm/services/agent_modification_tracker.py +841 -0
  96. claude_mpm/services/agent_profile_loader.py +606 -0
  97. claude_mpm/services/agent_registry.py +677 -0
  98. claude_mpm/services/base_agent_manager.py +380 -0
  99. claude_mpm/services/framework_agent_loader.py +337 -0
  100. claude_mpm/services/framework_claude_md_generator/README.md +92 -0
  101. claude_mpm/services/framework_claude_md_generator/__init__.py +206 -0
  102. claude_mpm/services/framework_claude_md_generator/content_assembler.py +151 -0
  103. claude_mpm/services/framework_claude_md_generator/content_validator.py +126 -0
  104. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +137 -0
  105. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +106 -0
  106. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +582 -0
  107. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +97 -0
  108. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +27 -0
  109. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +23 -0
  110. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +23 -0
  111. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +20 -0
  112. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +26 -0
  113. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +30 -0
  114. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +37 -0
  115. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +111 -0
  116. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +89 -0
  117. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +39 -0
  118. claude_mpm/services/framework_claude_md_generator/section_manager.py +106 -0
  119. claude_mpm/services/framework_claude_md_generator/version_manager.py +121 -0
  120. claude_mpm/services/framework_claude_md_generator.py +621 -0
  121. claude_mpm/services/hook_service.py +388 -0
  122. claude_mpm/services/hook_service_manager.py +223 -0
  123. claude_mpm/services/json_rpc_hook_manager.py +92 -0
  124. claude_mpm/services/parent_directory_manager/README.md +83 -0
  125. claude_mpm/services/parent_directory_manager/__init__.py +577 -0
  126. claude_mpm/services/parent_directory_manager/backup_manager.py +258 -0
  127. claude_mpm/services/parent_directory_manager/config_manager.py +210 -0
  128. claude_mpm/services/parent_directory_manager/deduplication_manager.py +279 -0
  129. claude_mpm/services/parent_directory_manager/framework_protector.py +143 -0
  130. claude_mpm/services/parent_directory_manager/operations.py +186 -0
  131. claude_mpm/services/parent_directory_manager/state_manager.py +624 -0
  132. claude_mpm/services/parent_directory_manager/template_deployer.py +579 -0
  133. claude_mpm/services/parent_directory_manager/validation_manager.py +378 -0
  134. claude_mpm/services/parent_directory_manager/version_control_helper.py +339 -0
  135. claude_mpm/services/parent_directory_manager/version_manager.py +222 -0
  136. claude_mpm/services/shared_prompt_cache.py +819 -0
  137. claude_mpm/services/ticket_manager.py +213 -0
  138. claude_mpm/services/ticket_manager_di.py +318 -0
  139. claude_mpm/services/ticketing_service_original.py +508 -0
  140. claude_mpm/services/version_control/VERSION +1 -0
  141. claude_mpm/services/version_control/__init__.py +70 -0
  142. claude_mpm/services/version_control/branch_strategy.py +670 -0
  143. claude_mpm/services/version_control/conflict_resolution.py +744 -0
  144. claude_mpm/services/version_control/git_operations.py +784 -0
  145. claude_mpm/services/version_control/semantic_versioning.py +703 -0
  146. claude_mpm/ui/__init__.py +1 -0
  147. claude_mpm/ui/rich_terminal_ui.py +295 -0
  148. claude_mpm/ui/terminal_ui.py +328 -0
  149. claude_mpm/utils/__init__.py +16 -0
  150. claude_mpm/utils/config_manager.py +468 -0
  151. claude_mpm/utils/import_migration_example.py +80 -0
  152. claude_mpm/utils/imports.py +182 -0
  153. claude_mpm/utils/path_operations.py +357 -0
  154. claude_mpm/utils/paths.py +289 -0
  155. claude_mpm-0.3.0.dist-info/METADATA +290 -0
  156. claude_mpm-0.3.0.dist-info/RECORD +159 -0
  157. claude_mpm-0.3.0.dist-info/WHEEL +5 -0
  158. claude_mpm-0.3.0.dist-info/entry_points.txt +4 -0
  159. claude_mpm-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,258 @@
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
+ )
@@ -0,0 +1,210 @@
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()