claude-mpm 3.4.26__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/INSTRUCTIONS.md +182 -299
- claude_mpm/agents/agent_loader.py +283 -57
- claude_mpm/agents/agent_loader_integration.py +6 -9
- claude_mpm/agents/base_agent.json +2 -1
- claude_mpm/agents/base_agent_loader.py +1 -1
- claude_mpm/cli/__init__.py +6 -10
- claude_mpm/cli/commands/__init__.py +0 -2
- claude_mpm/cli/commands/agents.py +1 -1
- claude_mpm/cli/commands/memory.py +1 -1
- claude_mpm/cli/commands/run.py +12 -0
- claude_mpm/cli/parser.py +0 -13
- claude_mpm/cli/utils.py +1 -1
- claude_mpm/config/__init__.py +44 -2
- claude_mpm/config/agent_config.py +348 -0
- claude_mpm/config/paths.py +322 -0
- claude_mpm/constants.py +0 -1
- claude_mpm/core/__init__.py +2 -5
- claude_mpm/core/agent_registry.py +63 -17
- claude_mpm/core/claude_runner.py +354 -43
- claude_mpm/core/config.py +7 -1
- claude_mpm/core/config_aliases.py +4 -3
- claude_mpm/core/config_paths.py +151 -0
- claude_mpm/core/factories.py +4 -50
- claude_mpm/core/logger.py +11 -13
- claude_mpm/core/service_registry.py +2 -2
- claude_mpm/dashboard/static/js/components/agent-inference.js +101 -25
- claude_mpm/dashboard/static/js/components/event-processor.js +3 -2
- claude_mpm/hooks/claude_hooks/hook_handler.py +343 -83
- claude_mpm/hooks/memory_integration_hook.py +1 -1
- claude_mpm/init.py +37 -6
- claude_mpm/scripts/socketio_daemon.py +6 -2
- claude_mpm/services/__init__.py +71 -3
- claude_mpm/services/agents/__init__.py +85 -0
- claude_mpm/services/agents/deployment/__init__.py +21 -0
- claude_mpm/services/{agent_deployment.py → agents/deployment/agent_deployment.py} +192 -41
- claude_mpm/services/{agent_lifecycle_manager.py → agents/deployment/agent_lifecycle_manager.py} +11 -10
- claude_mpm/services/agents/loading/__init__.py +11 -0
- claude_mpm/services/{agent_profile_loader.py → agents/loading/agent_profile_loader.py} +9 -8
- claude_mpm/services/{base_agent_manager.py → agents/loading/base_agent_manager.py} +2 -2
- claude_mpm/services/{framework_agent_loader.py → agents/loading/framework_agent_loader.py} +116 -40
- claude_mpm/services/agents/management/__init__.py +9 -0
- claude_mpm/services/{agent_management_service.py → agents/management/agent_management_service.py} +6 -5
- claude_mpm/services/agents/memory/__init__.py +21 -0
- claude_mpm/services/{agent_memory_manager.py → agents/memory/agent_memory_manager.py} +3 -3
- claude_mpm/services/agents/registry/__init__.py +29 -0
- claude_mpm/services/{agent_registry.py → agents/registry/agent_registry.py} +101 -16
- claude_mpm/services/{deployed_agent_discovery.py → agents/registry/deployed_agent_discovery.py} +12 -2
- claude_mpm/services/{agent_modification_tracker.py → agents/registry/modification_tracker.py} +6 -5
- claude_mpm/services/async_session_logger.py +584 -0
- claude_mpm/services/claude_session_logger.py +299 -0
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +2 -2
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +17 -17
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +3 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +1 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +1 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +19 -24
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +1 -1
- claude_mpm/services/framework_claude_md_generator.py +4 -2
- claude_mpm/services/memory/__init__.py +17 -0
- claude_mpm/services/{memory_builder.py → memory/builder.py} +3 -3
- claude_mpm/services/memory/cache/__init__.py +14 -0
- claude_mpm/services/{shared_prompt_cache.py → memory/cache/shared_prompt_cache.py} +1 -1
- claude_mpm/services/memory/cache/simple_cache.py +317 -0
- claude_mpm/services/{memory_optimizer.py → memory/optimizer.py} +1 -1
- claude_mpm/services/{memory_router.py → memory/router.py} +1 -1
- claude_mpm/services/optimized_hook_service.py +542 -0
- claude_mpm/services/project_registry.py +14 -8
- claude_mpm/services/response_tracker.py +237 -0
- claude_mpm/services/ticketing_service_original.py +4 -2
- claude_mpm/services/version_control/branch_strategy.py +3 -1
- claude_mpm/utils/paths.py +12 -10
- claude_mpm/utils/session_logging.py +114 -0
- claude_mpm/validation/agent_validator.py +2 -1
- {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/METADATA +26 -20
- {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/RECORD +83 -106
- claude_mpm/cli/commands/ui.py +0 -57
- claude_mpm/core/simple_runner.py +0 -1046
- claude_mpm/hooks/builtin/__init__.py +0 -1
- claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
- claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
- claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
- claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
- claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
- claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
- claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
- claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
- claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
- claude_mpm/orchestration/__init__.py +0 -6
- claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
- claude_mpm/orchestration/archive/factory.py +0 -215
- claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
- claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
- claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
- claude_mpm/orchestration/archive/orchestrator.py +0 -501
- claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
- claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
- claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
- claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
- claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
- claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
- claude_mpm/schemas/workflow_validator.py +0 -411
- claude_mpm/services/parent_directory_manager/__init__.py +0 -577
- claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
- claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
- claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
- claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
- claude_mpm/services/parent_directory_manager/operations.py +0 -186
- claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
- claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
- claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
- claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
- claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
- claude_mpm/ui/__init__.py +0 -1
- claude_mpm/ui/rich_terminal_ui.py +0 -295
- claude_mpm/ui/terminal_ui.py +0 -328
- /claude_mpm/services/{agent_versioning.py → agents/deployment/agent_versioning.py} +0 -0
- /claude_mpm/services/{agent_capabilities_generator.py → agents/management/agent_capabilities_generator.py} +0 -0
- /claude_mpm/services/{agent_persistence_service.py → agents/memory/agent_persistence_service.py} +0 -0
- {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/WHEEL +0 -0
- {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.4.26.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()
|