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.
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 +6 -10
  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.26.dist-info → claude_mpm-3.5.0.dist-info}/METADATA +26 -20
  76. {claude_mpm-3.4.26.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.26.dist-info → claude_mpm-3.5.0.dist-info}/WHEEL +0 -0
  121. {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/entry_points.txt +0 -0
  122. {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/licenses/LICENSE +0 -0
  123. {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/top_level.txt +0 -0
@@ -1,279 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Deduplication Manager - Handles INSTRUCTIONS.md/CLAUDE.md file deduplication in parent directory hierarchy
4
- ================================================================================
5
-
6
- This module manages the deduplication of INSTRUCTIONS.md (and legacy CLAUDE.md) files
7
- to prevent duplicate context loading in Claude Code.
8
-
9
- Business Logic:
10
- - Walk up the directory tree to find all INSTRUCTIONS.md and CLAUDE.md files
11
- - Identify framework deployment templates vs project files
12
- - Keep only the rootmost framework template
13
- - Preserve all project-specific INSTRUCTIONS.md/CLAUDE.md files
14
- """
15
-
16
- import os
17
- from pathlib import Path
18
- from typing import List, Tuple, Optional, Dict, Any
19
- from datetime import datetime
20
- import logging
21
-
22
-
23
- class DeduplicationManager:
24
- """Manages deduplication of INSTRUCTIONS.md/CLAUDE.md files in parent directories."""
25
-
26
- def __init__(self, logger: Optional[logging.Logger] = None):
27
- """
28
- Initialize the Deduplication Manager.
29
-
30
- Args:
31
- logger: Logger instance to use
32
- """
33
- self.logger = logger or logging.getLogger(__name__)
34
-
35
- async def deduplicate_claude_md_files(
36
- self,
37
- is_framework_template_func,
38
- extract_version_func,
39
- compare_versions_func,
40
- create_backup_func
41
- ) -> List[Tuple[Path, str]]:
42
- """
43
- Deduplicate INSTRUCTIONS.md/CLAUDE.md files in parent directory hierarchy.
44
-
45
- BUSINESS LOGIC:
46
- 1. Walk up the directory tree from current working directory to root
47
- 2. Find ALL INSTRUCTIONS.md and CLAUDE.md files in this parent hierarchy
48
- 3. Identify which files are framework deployment templates vs project files
49
- - Framework templates have title "# Claude PM Framework Configuration - Deployment"
50
- - Project files (like orchestration test projects) are preserved
51
- 4. Among framework templates, find the one with the newest version
52
- 5. Keep ONLY the rootmost (highest in hierarchy) framework template
53
- 6. Update the rootmost template to the newest version if needed
54
- 7. Backup and remove all other framework templates
55
- 8. Never touch project CLAUDE.md files
56
-
57
- RATIONALE:
58
- - Claude Code loads ALL CLAUDE.md files it finds in the directory tree
59
- - Multiple framework templates cause duplicated context
60
- - Only one framework template should exist at the rootmost location
61
- - Project-specific CLAUDE.md files serve different purposes and must be preserved
62
-
63
- Args:
64
- is_framework_template_func: Function to check if content is framework template
65
- extract_version_func: Function to extract version from content
66
- compare_versions_func: Function to compare two version strings
67
- create_backup_func: Function to create backup of a file
68
-
69
- Returns:
70
- List of tuples (original_path, action_taken) for logging
71
- """
72
- deduplication_actions = []
73
-
74
- try:
75
- self.logger.info("🔍 Starting INSTRUCTIONS.md/CLAUDE.md deduplication scan...")
76
-
77
- # STEP 1: Walk up the directory tree from current directory to root
78
- current_path = Path.cwd()
79
- claude_md_files = []
80
-
81
- # Collect ALL INSTRUCTIONS.md and CLAUDE.md files found while walking up parent directories
82
- while current_path != current_path.parent: # Stop at root
83
- # Check for INSTRUCTIONS.md first
84
- instructions_path = current_path / "INSTRUCTIONS.md"
85
- if instructions_path.exists() and instructions_path.is_file():
86
- claude_md_files.append(instructions_path)
87
- self.logger.debug(f"Found INSTRUCTIONS.md at: {instructions_path}")
88
- else:
89
- # Check for legacy CLAUDE.md
90
- claude_md_path = current_path / "CLAUDE.md"
91
- if claude_md_path.exists() and claude_md_path.is_file():
92
- claude_md_files.append(claude_md_path)
93
- self.logger.debug(f"Found CLAUDE.md at: {claude_md_path}")
94
- current_path = current_path.parent
95
-
96
- # Check root directory as well
97
- root_instructions = current_path / "INSTRUCTIONS.md"
98
- if root_instructions.exists() and root_instructions.is_file():
99
- claude_md_files.append(root_instructions)
100
- self.logger.debug(f"Found INSTRUCTIONS.md at root: {root_instructions}")
101
- else:
102
- # Check for legacy CLAUDE.md at root
103
- root_claude_md = current_path / "CLAUDE.md"
104
- if root_claude_md.exists() and root_claude_md.is_file():
105
- claude_md_files.append(root_claude_md)
106
- self.logger.debug(f"Found CLAUDE.md at root: {root_claude_md}")
107
-
108
- # If we found 0 or 1 files, there's nothing to deduplicate
109
- if len(claude_md_files) <= 1:
110
- self.logger.info("✅ No duplicate INSTRUCTIONS.md/CLAUDE.md files found in parent hierarchy")
111
- return deduplication_actions
112
-
113
- # STEP 2: Sort by path depth (rootmost first)
114
- claude_md_files.sort(key=lambda p: len(p.parts))
115
-
116
- self.logger.info(f"📊 Found {len(claude_md_files)} INSTRUCTIONS.md/CLAUDE.md files in parent hierarchy")
117
-
118
- # STEP 3: First pass - analyze all files to categorize and find newest version
119
- framework_templates = []
120
- newest_version = None
121
- newest_content = None
122
-
123
- for file_path in claude_md_files:
124
- try:
125
- content = file_path.read_text()
126
- # Check if this is a framework deployment template
127
- is_framework_template = is_framework_template_func(content)
128
-
129
- if is_framework_template:
130
- # Extract version from CLAUDE_MD_VERSION metadata
131
- version = extract_version_func(content)
132
- framework_templates.append((file_path, content, version))
133
-
134
- # Track the newest version we've seen
135
- if version and (newest_version is None or compare_versions_func(version, newest_version) > 0):
136
- newest_version = version
137
- newest_content = content
138
- self.logger.info(f"📋 Found newer version {version} at: {file_path}")
139
- except Exception as e:
140
- self.logger.error(f"Failed to analyze {file_path}: {e}")
141
-
142
- # STEP 4: Second pass - process files based on our analysis
143
- for idx, file_path in enumerate(claude_md_files):
144
- try:
145
- content = file_path.read_text()
146
- is_framework_template = is_framework_template_func(content)
147
-
148
- if idx == 0:
149
- # This is the ROOTMOST file
150
- if is_framework_template:
151
- current_version = extract_version_func(content)
152
-
153
- # Check if we found a newer version elsewhere that we should update to
154
- if newest_version and current_version and compare_versions_func(newest_version, current_version) > 0:
155
- # We found a newer version in a subdirectory - update the rootmost file
156
-
157
- # First backup the current rootmost file
158
- backup_path = await create_backup_func(file_path)
159
- if backup_path:
160
- self.logger.info(f"📁 Backed up current rootmost file before update: {backup_path}")
161
-
162
- # Update with the newest content we found
163
- file_path.write_text(newest_content)
164
- self.logger.info(f"⬆️ Updated rootmost template from {current_version} to {newest_version}")
165
- deduplication_actions.append((file_path, f"updated from {current_version} to {newest_version}"))
166
- else:
167
- # Rootmost file already has the newest version (or versions are equal)
168
- self.logger.info(f"✅ Keeping primary framework template at: {file_path} (version {current_version})")
169
- deduplication_actions.append((file_path, "kept as primary"))
170
- else:
171
- # Rootmost file is NOT a framework template - this is unusual but we preserve it
172
- self.logger.info(f"⚠️ File at {file_path} is not a framework template - skipping")
173
- deduplication_actions.append((file_path, "skipped - not framework template"))
174
- else:
175
- # This is NOT the rootmost file
176
- if is_framework_template:
177
- # This is a REDUNDANT framework template that must be removed
178
-
179
- # Generate backup filename with timestamp
180
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
181
- backup_name = f"CLAUDE.md.backup_{timestamp}"
182
- backup_path = file_path.parent / backup_name
183
-
184
- # Handle duplicate timestamps (rare but possible)
185
- counter = 1
186
- while backup_path.exists():
187
- backup_name = f"CLAUDE.md.backup_{timestamp}_{counter:02d}"
188
- backup_path = file_path.parent / backup_name
189
- counter += 1
190
-
191
- # Rename the duplicate to backup
192
- file_path.rename(backup_path)
193
- self.logger.warning(f"📦 Backed up duplicate framework template: {file_path} → {backup_path}")
194
- deduplication_actions.append((file_path, f"backed up to {backup_path.name}"))
195
- else:
196
- # This is a PROJECT-SPECIFIC CLAUDE.md file - NEVER REMOVE THESE
197
- self.logger.info(f"🛡️ Preserving non-framework file at: {file_path}")
198
- deduplication_actions.append((file_path, "preserved - not framework template"))
199
-
200
- except Exception as file_error:
201
- self.logger.error(f"Failed to process file {file_path}: {file_error}")
202
- deduplication_actions.append((file_path, f"error: {str(file_error)}"))
203
-
204
- # Log summary
205
- backed_up_count = sum(1 for _, action in deduplication_actions if "backed up" in action)
206
- if backed_up_count > 0:
207
- self.logger.info(f"✅ Deduplication complete: {backed_up_count} duplicate framework templates backed up")
208
- else:
209
- self.logger.info("✅ Deduplication complete: No framework template duplicates needed backing up")
210
-
211
- except Exception as e:
212
- self.logger.error(f"Failed during CLAUDE.md deduplication: {e}")
213
-
214
- return deduplication_actions
215
-
216
- async def deduplicate_parent_claude_md(
217
- self,
218
- is_framework_template_func,
219
- extract_version_func,
220
- compare_versions_func,
221
- create_backup_func
222
- ) -> Dict[str, Any]:
223
- """
224
- Public method to manually trigger CLAUDE.md deduplication in parent hierarchy.
225
-
226
- Args:
227
- is_framework_template_func: Function to check if content is framework template
228
- extract_version_func: Function to extract version from content
229
- compare_versions_func: Function to compare two version strings
230
- create_backup_func: Function to create backup of a file
231
-
232
- Returns:
233
- Dictionary with deduplication results
234
- """
235
- try:
236
- self.logger.info("🔧 Manual CLAUDE.md deduplication requested")
237
-
238
- # Run deduplication
239
- actions = await self.deduplicate_claude_md_files(
240
- is_framework_template_func,
241
- extract_version_func,
242
- compare_versions_func,
243
- create_backup_func
244
- )
245
-
246
- # Build result summary
247
- result = {
248
- "success": True,
249
- "timestamp": datetime.now().isoformat(),
250
- "actions_taken": len(actions),
251
- "details": []
252
- }
253
-
254
- for file_path, action in actions:
255
- result["details"].append({
256
- "file": str(file_path),
257
- "action": action
258
- })
259
-
260
- # Count different action types
261
- action_summary = {
262
- "kept_primary": sum(1 for _, a in actions if "kept as primary" in a),
263
- "backed_up": sum(1 for _, a in actions if "backed up" in a),
264
- "preserved": sum(1 for _, a in actions if "preserved" in a),
265
- "skipped": sum(1 for _, a in actions if "skipped" in a),
266
- "errors": sum(1 for _, a in actions if "error:" in a)
267
- }
268
-
269
- result["summary"] = action_summary
270
-
271
- return result
272
-
273
- except Exception as e:
274
- self.logger.error(f"Failed to run manual deduplication: {e}")
275
- return {
276
- "success": False,
277
- "error": str(e),
278
- "timestamp": datetime.now().isoformat()
279
- }
@@ -1,143 +0,0 @@
1
- """Framework protection module for critical framework files.
2
-
3
- This module provides the FrameworkProtector class which ensures critical
4
- framework files like framework/INSTRUCTIONS.md (legacy: framework/CLAUDE.md)
5
- are protected from accidental deletion and maintain proper permissions.
6
- """
7
-
8
- import os
9
- from pathlib import Path
10
- from typing import Optional
11
- import logging
12
-
13
-
14
- class FrameworkProtector:
15
- """Protects critical framework files from deletion and corruption."""
16
-
17
- def __init__(self, framework_path: Path, logger: Optional[logging.Logger] = None):
18
- """Initialize the FrameworkProtector.
19
-
20
- Args:
21
- framework_path: Path to the framework directory
22
- logger: Optional logger instance for logging protection activities
23
- """
24
- self.framework_path = framework_path
25
- self.logger = logger or logging.getLogger(__name__)
26
-
27
- def protect_framework_template(self, framework_template_path: Path) -> None:
28
- """Ensure framework template is protected from deletion.
29
-
30
- This method verifies that the framework template exists, is the correct
31
- file, and has proper read permissions. This is critical to prevent
32
- accidental deletion of the master framework template.
33
-
34
- Args:
35
- framework_template_path: Path to the framework template file
36
- """
37
- try:
38
- # Verify the file exists
39
- if not framework_template_path.exists():
40
- self.logger.warning(
41
- f"Framework template not found at {framework_template_path}"
42
- )
43
- return
44
-
45
- # Verify this is actually the framework template
46
- # Check that the filename is INSTRUCTIONS.md or CLAUDE.md (legacy) and path contains "framework"
47
- if (framework_template_path.name not in ["INSTRUCTIONS.md", "CLAUDE.md"] or
48
- "framework" not in str(framework_template_path)):
49
- self.logger.debug(
50
- f"Path {framework_template_path} does not appear to be "
51
- "the framework template"
52
- )
53
- return
54
-
55
- # Ensure file has proper permissions (readable)
56
- try:
57
- # Get current permissions
58
- current_mode = framework_template_path.stat().st_mode
59
-
60
- # Ensure owner can read (add read permission if needed)
61
- if not (current_mode & 0o400):
62
- new_mode = current_mode | 0o400 # Add owner read
63
- os.chmod(framework_template_path, new_mode)
64
- self.logger.info(
65
- f"Added read permission to framework template at "
66
- f"{framework_template_path}"
67
- )
68
-
69
- # Log successful protection
70
- self.logger.debug(
71
- f"Framework template protected at {framework_template_path}"
72
- )
73
-
74
- except Exception as e:
75
- self.logger.error(
76
- f"Failed to set permissions on framework template: {e}"
77
- )
78
-
79
- except Exception as e:
80
- self.logger.error(
81
- f"Error protecting framework template at "
82
- f"{framework_template_path}: {e}"
83
- )
84
-
85
- def validate_framework_template_integrity(self, content: str) -> bool:
86
- """Validate that framework template has proper structure and metadata.
87
-
88
- This method checks that the template contains essential metadata
89
- and structure required for proper framework operation.
90
-
91
- Args:
92
- content: The content of the framework template to validate
93
-
94
- Returns:
95
- bool: True if template is valid, False otherwise
96
- """
97
- try:
98
- # Check for essential metadata markers
99
- required_markers = [
100
- "CLAUDE_MD_VERSION:",
101
- "FRAMEWORK_VERSION:",
102
- "AI ASSISTANT ROLE DESIGNATION",
103
- "AGENTS",
104
- "TODO AND TASK TOOLS"
105
- ]
106
-
107
- # Check each required marker exists in content
108
- for marker in required_markers:
109
- if marker not in content:
110
- self.logger.warning(
111
- f"Framework template missing required marker: {marker}"
112
- )
113
- return False
114
-
115
- # Check for handlebars variables that should be present
116
- required_variables = [
117
- "{{DEPLOYMENT_ID}}",
118
- "{{PLATFORM_NOTES}}"
119
- ]
120
-
121
- for variable in required_variables:
122
- if variable not in content:
123
- self.logger.warning(
124
- f"Framework template missing required variable: {variable}"
125
- )
126
- return False
127
-
128
- # Validate minimum content length (templates should be substantial)
129
- min_content_length = 1000 # Reasonable minimum for a valid template
130
- if len(content) < min_content_length:
131
- self.logger.warning(
132
- f"Framework template content too short: {len(content)} chars "
133
- f"(minimum: {min_content_length})"
134
- )
135
- return False
136
-
137
- # If all checks pass, template is valid
138
- self.logger.debug("Framework template integrity validated successfully")
139
- return True
140
-
141
- except Exception as e:
142
- self.logger.error(f"Error validating framework template integrity: {e}")
143
- return False
@@ -1,186 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Parent Directory Operations - Handles directory context detection and registration
4
- ================================================================================
5
-
6
- This module manages parent directory context detection, auto-registration,
7
- and template variable generation.
8
- """
9
-
10
- import os
11
- from pathlib import Path
12
- from typing import List, Dict, Any, Optional
13
- from datetime import datetime
14
- from enum import Enum
15
- import logging
16
-
17
-
18
- class ParentDirectoryContext(Enum):
19
- """Context types for parent directory management."""
20
- DEPLOYMENT_ROOT = "deployment_root"
21
- PROJECT_COLLECTION = "project_collection"
22
- WORKSPACE_ROOT = "workspace_root"
23
- USER_HOME = "user_home"
24
- CUSTOM = "custom"
25
-
26
-
27
- class ParentDirectoryOperations:
28
- """Manages parent directory detection and operations."""
29
-
30
- def __init__(self, logger: Optional[logging.Logger] = None):
31
- """
32
- Initialize the Parent Directory Operations.
33
-
34
- Args:
35
- logger: Logger instance to use
36
- """
37
- self.logger = logger or logging.getLogger(__name__)
38
-
39
- async def detect_parent_directory_context(
40
- self, target_directory: Path
41
- ) -> ParentDirectoryContext:
42
- """
43
- Detect the context of a parent directory.
44
-
45
- Args:
46
- target_directory: Directory to analyze
47
-
48
- Returns:
49
- ParentDirectoryContext enum value
50
- """
51
- try:
52
- # Check if it's the user home directory
53
- if target_directory == Path.home():
54
- return ParentDirectoryContext.USER_HOME
55
-
56
- # Check if it contains a deployment (has claude-multiagent-pm subdirectory)
57
- if (target_directory / "claude-multiagent-pm").exists():
58
- return ParentDirectoryContext.DEPLOYMENT_ROOT
59
-
60
- # Check if it contains multiple projects
61
- subdirs = [d for d in target_directory.iterdir() if d.is_dir()]
62
- project_indicators = [".git", "package.json", "pyproject.toml", "Cargo.toml"]
63
-
64
- project_count = 0
65
- for subdir in subdirs:
66
- if any((subdir / indicator).exists() for indicator in project_indicators):
67
- project_count += 1
68
-
69
- if project_count > 1:
70
- return ParentDirectoryContext.PROJECT_COLLECTION
71
-
72
- # Check if it's a workspace root
73
- workspace_indicators = [".vscode", ".idea", "workspace.json"]
74
- if any((target_directory / indicator).exists() for indicator in workspace_indicators):
75
- return ParentDirectoryContext.WORKSPACE_ROOT
76
-
77
- # Default to custom
78
- return ParentDirectoryContext.CUSTOM
79
-
80
- except Exception as e:
81
- self.logger.error(
82
- f"Failed to detect parent directory context for {target_directory}: {e}"
83
- )
84
- return ParentDirectoryContext.CUSTOM
85
-
86
- async def auto_register_parent_directories(
87
- self,
88
- search_paths: List[Path],
89
- template_id: str,
90
- register_func,
91
- get_default_variables_func
92
- ) -> List[Path]:
93
- """
94
- Automatically register parent directories that should be managed.
95
-
96
- Args:
97
- search_paths: Paths to search for parent directories
98
- template_id: Template to use for auto-registration
99
- register_func: Function to register directory
100
- get_default_variables_func: Function to get default template variables
101
-
102
- Returns:
103
- List of registered directories
104
- """
105
- try:
106
- registered_directories = []
107
-
108
- for search_path in search_paths:
109
- if not search_path.exists() or not search_path.is_dir():
110
- continue
111
-
112
- # Check if this directory should be managed
113
- context = await self.detect_parent_directory_context(search_path)
114
-
115
- # Skip user home directory unless explicitly configured
116
- if context == ParentDirectoryContext.USER_HOME:
117
- continue
118
-
119
- # Auto-register if it looks like a deployment root or project collection
120
- if context in [
121
- ParentDirectoryContext.DEPLOYMENT_ROOT,
122
- ParentDirectoryContext.PROJECT_COLLECTION,
123
- ]:
124
- success = await register_func(
125
- search_path,
126
- context,
127
- template_id,
128
- get_default_variables_func(search_path, context),
129
- )
130
-
131
- if success:
132
- registered_directories.append(search_path)
133
- self.logger.info(f"Auto-registered parent directory: {search_path}")
134
-
135
- return registered_directories
136
-
137
- except Exception as e:
138
- self.logger.error(f"Failed to auto-register parent directories: {e}")
139
- return []
140
-
141
- def get_default_template_variables(
142
- self,
143
- target_directory: Path,
144
- context: ParentDirectoryContext,
145
- deployment_context: Optional[Dict[str, Any]] = None
146
- ) -> Dict[str, Any]:
147
- """Get default template variables for a directory."""
148
- variables = {
149
- "DIRECTORY_PATH": str(target_directory),
150
- "DIRECTORY_NAME": target_directory.name,
151
- "CONTEXT": context.value,
152
- "TIMESTAMP": datetime.now().isoformat(),
153
- "PLATFORM": os.name,
154
- }
155
-
156
- # Add deployment-specific variables if available
157
- if deployment_context:
158
- variables.update(
159
- {
160
- "DEPLOYMENT_TYPE": deployment_context.get("strategy", "unknown"),
161
- "DEPLOYMENT_PLATFORM": deployment_context.get("config", {}).get(
162
- "platform", "unknown"
163
- ),
164
- }
165
- )
166
-
167
- return variables
168
-
169
- def get_platform_notes(self) -> str:
170
- """
171
- Get platform-specific notes for the framework template.
172
-
173
- Returns:
174
- Platform-specific notes string
175
- """
176
- import platform
177
- system = platform.system().lower()
178
-
179
- if system == 'windows':
180
- return "Windows users may need to use 'python' instead of 'python3' depending on installation."
181
- elif system == 'darwin':
182
- return "macOS users should ensure python3 is installed via Homebrew or official Python installer."
183
- elif system == 'linux':
184
- return "Linux users may need to install python3 via their package manager if not present."
185
- else:
186
- return f"Platform-specific configuration may be required for {system}."