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.
- claude_mpm/__init__.py +17 -0
- claude_mpm/__main__.py +14 -0
- claude_mpm/_version.py +32 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +88 -0
- claude_mpm/agents/INSTRUCTIONS.md +375 -0
- claude_mpm/agents/__init__.py +118 -0
- claude_mpm/agents/agent_loader.py +621 -0
- claude_mpm/agents/agent_loader_integration.py +229 -0
- claude_mpm/agents/agents_metadata.py +204 -0
- claude_mpm/agents/base_agent.json +27 -0
- claude_mpm/agents/base_agent_loader.py +519 -0
- claude_mpm/agents/schema/agent_schema.json +160 -0
- claude_mpm/agents/system_agent_config.py +587 -0
- claude_mpm/agents/templates/__init__.py +101 -0
- claude_mpm/agents/templates/data_engineer_agent.json +46 -0
- claude_mpm/agents/templates/documentation_agent.json +45 -0
- claude_mpm/agents/templates/engineer_agent.json +49 -0
- claude_mpm/agents/templates/ops_agent.json +46 -0
- claude_mpm/agents/templates/qa_agent.json +45 -0
- claude_mpm/agents/templates/research_agent.json +49 -0
- claude_mpm/agents/templates/security_agent.json +46 -0
- claude_mpm/agents/templates/update-optimized-specialized-agents.json +374 -0
- claude_mpm/agents/templates/version_control_agent.json +46 -0
- claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +6 -0
- claude_mpm/cli.py +655 -0
- claude_mpm/cli_main.py +13 -0
- claude_mpm/cli_module/__init__.py +15 -0
- claude_mpm/cli_module/args.py +222 -0
- claude_mpm/cli_module/commands.py +203 -0
- claude_mpm/cli_module/migration_example.py +183 -0
- claude_mpm/cli_module/refactoring_guide.md +253 -0
- claude_mpm/cli_old/__init__.py +1 -0
- claude_mpm/cli_old/ticket_cli.py +102 -0
- claude_mpm/config/__init__.py +5 -0
- claude_mpm/config/hook_config.py +42 -0
- claude_mpm/constants.py +150 -0
- claude_mpm/core/__init__.py +45 -0
- claude_mpm/core/agent_name_normalizer.py +248 -0
- claude_mpm/core/agent_registry.py +627 -0
- claude_mpm/core/agent_registry.py.bak +312 -0
- claude_mpm/core/agent_session_manager.py +273 -0
- claude_mpm/core/base_service.py +747 -0
- claude_mpm/core/base_service.py.bak +406 -0
- claude_mpm/core/config.py +334 -0
- claude_mpm/core/config_aliases.py +292 -0
- claude_mpm/core/container.py +347 -0
- claude_mpm/core/factories.py +281 -0
- claude_mpm/core/framework_loader.py +472 -0
- claude_mpm/core/injectable_service.py +206 -0
- claude_mpm/core/interfaces.py +539 -0
- claude_mpm/core/logger.py +468 -0
- claude_mpm/core/minimal_framework_loader.py +107 -0
- claude_mpm/core/mixins.py +150 -0
- claude_mpm/core/service_registry.py +299 -0
- claude_mpm/core/session_manager.py +190 -0
- claude_mpm/core/simple_runner.py +511 -0
- claude_mpm/core/tool_access_control.py +173 -0
- claude_mpm/hooks/README.md +243 -0
- claude_mpm/hooks/__init__.py +5 -0
- claude_mpm/hooks/base_hook.py +154 -0
- claude_mpm/hooks/builtin/__init__.py +1 -0
- claude_mpm/hooks/builtin/logging_hook_example.py +165 -0
- claude_mpm/hooks/builtin/post_delegation_hook_example.py +124 -0
- claude_mpm/hooks/builtin/pre_delegation_hook_example.py +125 -0
- claude_mpm/hooks/builtin/submit_hook_example.py +100 -0
- claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +237 -0
- claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +239 -0
- claude_mpm/hooks/builtin/workflow_start_hook.py +181 -0
- claude_mpm/hooks/hook_client.py +264 -0
- claude_mpm/hooks/hook_runner.py +370 -0
- claude_mpm/hooks/json_rpc_executor.py +259 -0
- claude_mpm/hooks/json_rpc_hook_client.py +319 -0
- claude_mpm/hooks/tool_call_interceptor.py +204 -0
- claude_mpm/init.py +246 -0
- claude_mpm/orchestration/SUBPROCESS_DESIGN.md +66 -0
- claude_mpm/orchestration/__init__.py +6 -0
- claude_mpm/orchestration/archive/direct_orchestrator.py +195 -0
- claude_mpm/orchestration/archive/factory.py +215 -0
- claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +188 -0
- claude_mpm/orchestration/archive/hook_integration_example.py +178 -0
- claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +826 -0
- claude_mpm/orchestration/archive/orchestrator.py +501 -0
- claude_mpm/orchestration/archive/pexpect_orchestrator.py +252 -0
- claude_mpm/orchestration/archive/pty_orchestrator.py +270 -0
- claude_mpm/orchestration/archive/simple_orchestrator.py +82 -0
- claude_mpm/orchestration/archive/subprocess_orchestrator.py +801 -0
- claude_mpm/orchestration/archive/system_prompt_orchestrator.py +278 -0
- claude_mpm/orchestration/archive/wrapper_orchestrator.py +187 -0
- claude_mpm/scripts/__init__.py +1 -0
- claude_mpm/scripts/ticket.py +269 -0
- claude_mpm/services/__init__.py +10 -0
- claude_mpm/services/agent_deployment.py +955 -0
- claude_mpm/services/agent_lifecycle_manager.py +948 -0
- claude_mpm/services/agent_management_service.py +596 -0
- claude_mpm/services/agent_modification_tracker.py +841 -0
- claude_mpm/services/agent_profile_loader.py +606 -0
- claude_mpm/services/agent_registry.py +677 -0
- claude_mpm/services/base_agent_manager.py +380 -0
- claude_mpm/services/framework_agent_loader.py +337 -0
- claude_mpm/services/framework_claude_md_generator/README.md +92 -0
- claude_mpm/services/framework_claude_md_generator/__init__.py +206 -0
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +151 -0
- claude_mpm/services/framework_claude_md_generator/content_validator.py +126 -0
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +137 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +106 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +582 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +97 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +27 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +23 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +23 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +20 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +26 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +30 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +37 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +111 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +89 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +39 -0
- claude_mpm/services/framework_claude_md_generator/section_manager.py +106 -0
- claude_mpm/services/framework_claude_md_generator/version_manager.py +121 -0
- claude_mpm/services/framework_claude_md_generator.py +621 -0
- claude_mpm/services/hook_service.py +388 -0
- claude_mpm/services/hook_service_manager.py +223 -0
- claude_mpm/services/json_rpc_hook_manager.py +92 -0
- claude_mpm/services/parent_directory_manager/README.md +83 -0
- claude_mpm/services/parent_directory_manager/__init__.py +577 -0
- claude_mpm/services/parent_directory_manager/backup_manager.py +258 -0
- claude_mpm/services/parent_directory_manager/config_manager.py +210 -0
- claude_mpm/services/parent_directory_manager/deduplication_manager.py +279 -0
- claude_mpm/services/parent_directory_manager/framework_protector.py +143 -0
- claude_mpm/services/parent_directory_manager/operations.py +186 -0
- claude_mpm/services/parent_directory_manager/state_manager.py +624 -0
- claude_mpm/services/parent_directory_manager/template_deployer.py +579 -0
- claude_mpm/services/parent_directory_manager/validation_manager.py +378 -0
- claude_mpm/services/parent_directory_manager/version_control_helper.py +339 -0
- claude_mpm/services/parent_directory_manager/version_manager.py +222 -0
- claude_mpm/services/shared_prompt_cache.py +819 -0
- claude_mpm/services/ticket_manager.py +213 -0
- claude_mpm/services/ticket_manager_di.py +318 -0
- claude_mpm/services/ticketing_service_original.py +508 -0
- claude_mpm/services/version_control/VERSION +1 -0
- claude_mpm/services/version_control/__init__.py +70 -0
- claude_mpm/services/version_control/branch_strategy.py +670 -0
- claude_mpm/services/version_control/conflict_resolution.py +744 -0
- claude_mpm/services/version_control/git_operations.py +784 -0
- claude_mpm/services/version_control/semantic_versioning.py +703 -0
- claude_mpm/ui/__init__.py +1 -0
- claude_mpm/ui/rich_terminal_ui.py +295 -0
- claude_mpm/ui/terminal_ui.py +328 -0
- claude_mpm/utils/__init__.py +16 -0
- claude_mpm/utils/config_manager.py +468 -0
- claude_mpm/utils/import_migration_example.py +80 -0
- claude_mpm/utils/imports.py +182 -0
- claude_mpm/utils/path_operations.py +357 -0
- claude_mpm/utils/paths.py +289 -0
- claude_mpm-0.3.0.dist-info/METADATA +290 -0
- claude_mpm-0.3.0.dist-info/RECORD +159 -0
- claude_mpm-0.3.0.dist-info/WHEEL +5 -0
- claude_mpm-0.3.0.dist-info/entry_points.txt +4 -0
- claude_mpm-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Content assembly for framework CLAUDE.md templates.
|
|
3
|
+
|
|
4
|
+
Assembles sections and applies template variable substitution.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import hashlib
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import Dict, List, Optional, Any
|
|
10
|
+
from collections import OrderedDict
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ContentAssembler:
|
|
14
|
+
"""Assembles framework CLAUDE.md content from sections."""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
"""Initialize content assembler."""
|
|
18
|
+
self.template_variables = {}
|
|
19
|
+
|
|
20
|
+
def generate_content_hash(self) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Generate a content hash for integrity verification.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
str: 16-character hash of content
|
|
26
|
+
"""
|
|
27
|
+
timestamp = datetime.utcnow().isoformat()
|
|
28
|
+
hash_obj = hashlib.sha256(timestamp.encode())
|
|
29
|
+
return hash_obj.hexdigest()[:16]
|
|
30
|
+
|
|
31
|
+
def assemble_content(self,
|
|
32
|
+
sections: OrderedDict,
|
|
33
|
+
template_variables: Optional[Dict[str, str]] = None) -> str:
|
|
34
|
+
"""
|
|
35
|
+
Assemble complete content from sections.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
sections: OrderedDict of section_name -> (generator_func, section_data)
|
|
39
|
+
template_variables: Variables to substitute in the template
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
str: Complete assembled content
|
|
43
|
+
"""
|
|
44
|
+
# Store template variables
|
|
45
|
+
if template_variables:
|
|
46
|
+
self.template_variables = template_variables
|
|
47
|
+
|
|
48
|
+
# Generate all sections
|
|
49
|
+
content_parts = []
|
|
50
|
+
for section_name, (generator_func, section_data) in sections.items():
|
|
51
|
+
section_content = generator_func(section_data)
|
|
52
|
+
content_parts.append(section_content)
|
|
53
|
+
|
|
54
|
+
# Join all sections
|
|
55
|
+
full_content = '\n'.join(content_parts)
|
|
56
|
+
|
|
57
|
+
# Apply template variable substitution
|
|
58
|
+
full_content = self.apply_template_variables(full_content)
|
|
59
|
+
|
|
60
|
+
return full_content
|
|
61
|
+
|
|
62
|
+
def apply_template_variables(self, content: str) -> str:
|
|
63
|
+
"""
|
|
64
|
+
Apply template variable substitution to content.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
content: Content with template variables
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
str: Content with variables substituted
|
|
71
|
+
"""
|
|
72
|
+
for var_name, var_value in self.template_variables.items():
|
|
73
|
+
placeholder = f"{{{{{var_name}}}}}"
|
|
74
|
+
content = content.replace(placeholder, var_value)
|
|
75
|
+
|
|
76
|
+
return content
|
|
77
|
+
|
|
78
|
+
def merge_sections(self,
|
|
79
|
+
base_sections: OrderedDict,
|
|
80
|
+
custom_sections: Dict[str, tuple]) -> OrderedDict:
|
|
81
|
+
"""
|
|
82
|
+
Merge custom sections with base sections.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
base_sections: Base section definitions
|
|
86
|
+
custom_sections: Custom sections to merge
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
OrderedDict: Merged sections
|
|
90
|
+
"""
|
|
91
|
+
merged = OrderedDict(base_sections)
|
|
92
|
+
|
|
93
|
+
for section_name, section_def in custom_sections.items():
|
|
94
|
+
merged[section_name] = section_def
|
|
95
|
+
|
|
96
|
+
return merged
|
|
97
|
+
|
|
98
|
+
def insert_section_after(self,
|
|
99
|
+
sections: OrderedDict,
|
|
100
|
+
new_section_name: str,
|
|
101
|
+
new_section_def: tuple,
|
|
102
|
+
after_section: str) -> OrderedDict:
|
|
103
|
+
"""
|
|
104
|
+
Insert a new section after a specified section.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
sections: Current sections
|
|
108
|
+
new_section_name: Name of new section
|
|
109
|
+
new_section_def: Definition tuple for new section
|
|
110
|
+
after_section: Section to insert after
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
OrderedDict: Updated sections
|
|
114
|
+
"""
|
|
115
|
+
if after_section not in sections:
|
|
116
|
+
# If target section doesn't exist, just append
|
|
117
|
+
sections[new_section_name] = new_section_def
|
|
118
|
+
return sections
|
|
119
|
+
|
|
120
|
+
new_sections = OrderedDict()
|
|
121
|
+
for key, value in sections.items():
|
|
122
|
+
new_sections[key] = value
|
|
123
|
+
if key == after_section:
|
|
124
|
+
new_sections[new_section_name] = new_section_def
|
|
125
|
+
|
|
126
|
+
return new_sections
|
|
127
|
+
|
|
128
|
+
def create_metadata_dict(self,
|
|
129
|
+
version: str,
|
|
130
|
+
framework_version: str,
|
|
131
|
+
content_hash: Optional[str] = None) -> Dict[str, Any]:
|
|
132
|
+
"""
|
|
133
|
+
Create metadata dictionary for header generation.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
version: CLAUDE_MD_VERSION string
|
|
137
|
+
framework_version: Framework version
|
|
138
|
+
content_hash: Optional content hash
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Dict: Metadata for header
|
|
142
|
+
"""
|
|
143
|
+
timestamp = datetime.utcnow().isoformat()
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
'version': version,
|
|
147
|
+
'framework_version': framework_version,
|
|
148
|
+
'deployment_date': timestamp,
|
|
149
|
+
'last_updated': timestamp,
|
|
150
|
+
'content_hash': content_hash or self.generate_content_hash()
|
|
151
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Content validation for framework CLAUDE.md templates.
|
|
3
|
+
|
|
4
|
+
Validates generated content structure and completeness.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from typing import Tuple, List, Set
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ContentValidator:
|
|
12
|
+
"""Validates framework CLAUDE.md content for completeness and correctness."""
|
|
13
|
+
|
|
14
|
+
# Required sections that must be present
|
|
15
|
+
REQUIRED_SECTIONS = [
|
|
16
|
+
(r'CLAUDE_MD_VERSION:', 'Version metadata'),
|
|
17
|
+
(r'## 🤖 AI ASSISTANT ROLE DESIGNATION', 'Role designation section'),
|
|
18
|
+
(r'## A\) AGENTS', 'Agents section'),
|
|
19
|
+
(r'## B\) TODO AND TASK TOOLS', 'Todo/Task tools section'),
|
|
20
|
+
(r'## C\) CLAUDE-PM INIT', 'Claude-PM init section'),
|
|
21
|
+
(r'## 🚨 CORE ORCHESTRATION PRINCIPLES', 'Orchestration principles'),
|
|
22
|
+
(r'## 🔥🚨 CRITICAL: SUBPROCESS VALIDATION PROTOCOL', 'Subprocess validation'),
|
|
23
|
+
(r'## 🚨 CRITICAL DELEGATION CONSTRAINTS', 'Delegation constraints'),
|
|
24
|
+
(r'## 🚨 TROUBLESHOOTING', 'Troubleshooting section'),
|
|
25
|
+
(r'## Core Responsibilities', 'Core responsibilities'),
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
# Template variables that are allowed to remain unsubstituted for runtime
|
|
29
|
+
ALLOWED_RUNTIME_VARS = {
|
|
30
|
+
'{{DEPLOYMENT_ID}}',
|
|
31
|
+
'{{PLATFORM}}',
|
|
32
|
+
'{{PYTHON_CMD}}',
|
|
33
|
+
'{{PLATFORM_NOTES}}'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
def validate_content(self, content: str) -> Tuple[bool, List[str]]:
|
|
37
|
+
"""
|
|
38
|
+
Validate that generated content has all required sections.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
content: Content to validate
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Tuple of (is_valid, list_of_issues)
|
|
45
|
+
"""
|
|
46
|
+
issues = []
|
|
47
|
+
|
|
48
|
+
# Check for required sections
|
|
49
|
+
for pattern, section_name in self.REQUIRED_SECTIONS:
|
|
50
|
+
if not re.search(pattern, content):
|
|
51
|
+
issues.append(f"Missing required section: {section_name}")
|
|
52
|
+
|
|
53
|
+
# Check for unsubstituted template variables
|
|
54
|
+
unsubstituted = re.findall(r'\{\{[^}]+\}\}', content)
|
|
55
|
+
unexpected_vars = [var for var in unsubstituted if var not in self.ALLOWED_RUNTIME_VARS]
|
|
56
|
+
if unexpected_vars:
|
|
57
|
+
issues.append(f"Unsubstituted template variables: {', '.join(set(unexpected_vars))}")
|
|
58
|
+
|
|
59
|
+
# Validate version format - now accepts simple serial numbers
|
|
60
|
+
version_match = re.search(r'CLAUDE_MD_VERSION:\s*(\d+)(?:-(\d+))?', content)
|
|
61
|
+
if not version_match:
|
|
62
|
+
issues.append("Invalid or missing CLAUDE_MD_VERSION format")
|
|
63
|
+
|
|
64
|
+
# Check for proper structure
|
|
65
|
+
if not content.strip():
|
|
66
|
+
issues.append("Content is empty")
|
|
67
|
+
|
|
68
|
+
# Validate agent section completeness
|
|
69
|
+
if '## A) AGENTS' in content:
|
|
70
|
+
required_agents = [
|
|
71
|
+
'Documentation Agent',
|
|
72
|
+
'Ticketing Agent',
|
|
73
|
+
'Version Control Agent',
|
|
74
|
+
'QA Agent',
|
|
75
|
+
'Research Agent',
|
|
76
|
+
'Ops Agent',
|
|
77
|
+
'Security Agent',
|
|
78
|
+
'Engineer Agent',
|
|
79
|
+
'Data Engineer Agent'
|
|
80
|
+
]
|
|
81
|
+
for agent in required_agents:
|
|
82
|
+
if agent not in content:
|
|
83
|
+
issues.append(f"Missing core agent: {agent}")
|
|
84
|
+
|
|
85
|
+
return len(issues) == 0, issues
|
|
86
|
+
|
|
87
|
+
def validate_section_order(self, sections: List[str]) -> Tuple[bool, List[str]]:
|
|
88
|
+
"""
|
|
89
|
+
Validate that sections are in the correct order.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
sections: List of section names in order
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Tuple of (is_valid, list_of_issues)
|
|
96
|
+
"""
|
|
97
|
+
expected_order = [
|
|
98
|
+
'header',
|
|
99
|
+
'role_designation',
|
|
100
|
+
'agents',
|
|
101
|
+
'todo_task_tools',
|
|
102
|
+
'claude_pm_init',
|
|
103
|
+
'orchestration_principles',
|
|
104
|
+
'subprocess_validation',
|
|
105
|
+
'delegation_constraints',
|
|
106
|
+
'environment_config',
|
|
107
|
+
'troubleshooting',
|
|
108
|
+
'core_responsibilities',
|
|
109
|
+
'footer'
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
issues = []
|
|
113
|
+
|
|
114
|
+
# Check that all expected sections are present
|
|
115
|
+
missing = set(expected_order) - set(sections)
|
|
116
|
+
if missing:
|
|
117
|
+
issues.append(f"Missing sections: {', '.join(missing)}")
|
|
118
|
+
|
|
119
|
+
# Check order (only for sections that exist in both lists)
|
|
120
|
+
common_sections = [s for s in sections if s in expected_order]
|
|
121
|
+
expected_common = [s for s in expected_order if s in sections]
|
|
122
|
+
|
|
123
|
+
if common_sections != expected_common:
|
|
124
|
+
issues.append("Sections are not in the expected order")
|
|
125
|
+
|
|
126
|
+
return len(issues) == 0, issues
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Deployment management for framework CLAUDE.md templates.
|
|
3
|
+
|
|
4
|
+
Handles deployment operations to parent directories.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Tuple, Optional
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from .version_manager import VersionManager
|
|
11
|
+
from .content_validator import ContentValidator
|
|
12
|
+
|
|
13
|
+
# Import framework detection utilities
|
|
14
|
+
from ...utils.framework_detection import is_framework_source_directory
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DeploymentManager:
|
|
18
|
+
"""Manages deployment of framework CLAUDE.md to parent directories."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, version_manager: VersionManager, validator: ContentValidator):
|
|
21
|
+
"""
|
|
22
|
+
Initialize deployment manager.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
version_manager: Version management instance
|
|
26
|
+
validator: Content validator instance
|
|
27
|
+
"""
|
|
28
|
+
self.version_manager = version_manager
|
|
29
|
+
self.validator = validator
|
|
30
|
+
|
|
31
|
+
def deploy_to_parent(self,
|
|
32
|
+
content: str,
|
|
33
|
+
parent_path: Path,
|
|
34
|
+
force: bool = False) -> Tuple[bool, str]:
|
|
35
|
+
"""
|
|
36
|
+
Deploy generated content to a parent directory.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
content: Content to deploy
|
|
40
|
+
parent_path: Path to parent directory
|
|
41
|
+
force: Force deployment even if versions match
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Tuple of (success, message)
|
|
45
|
+
"""
|
|
46
|
+
# Check if we're in the framework source directory
|
|
47
|
+
is_framework, markers = is_framework_source_directory(parent_path)
|
|
48
|
+
if is_framework:
|
|
49
|
+
return True, f"Skipping deployment - detected framework source directory (markers: {', '.join(markers)})"
|
|
50
|
+
|
|
51
|
+
# Use INSTRUCTIONS.md as primary, with CLAUDE.md as fallback
|
|
52
|
+
target_file = parent_path / "INSTRUCTIONS.md"
|
|
53
|
+
# TODO: Make this configurable via parameter
|
|
54
|
+
|
|
55
|
+
# Validate content before deployment
|
|
56
|
+
is_valid, issues = self.validator.validate_content(content)
|
|
57
|
+
if not is_valid:
|
|
58
|
+
return False, f"Validation failed: {'; '.join(issues)}"
|
|
59
|
+
|
|
60
|
+
# Check if file exists and compare versions
|
|
61
|
+
if target_file.exists() and not force:
|
|
62
|
+
with open(target_file, 'r') as f:
|
|
63
|
+
existing_content = f.read()
|
|
64
|
+
existing_fw_ver, _ = self.version_manager.parse_current_version(existing_content)
|
|
65
|
+
|
|
66
|
+
if existing_fw_ver == self.version_manager.framework_version:
|
|
67
|
+
return True, f"Version {existing_fw_ver} already deployed"
|
|
68
|
+
|
|
69
|
+
# Deploy
|
|
70
|
+
try:
|
|
71
|
+
# Ensure parent directory exists
|
|
72
|
+
parent_path.mkdir(parents=True, exist_ok=True)
|
|
73
|
+
|
|
74
|
+
# Write content
|
|
75
|
+
with open(target_file, 'w') as f:
|
|
76
|
+
f.write(content)
|
|
77
|
+
|
|
78
|
+
# Get version info for success message
|
|
79
|
+
fw_ver, serial = self.version_manager.parse_current_version(content)
|
|
80
|
+
version_str = f"{fw_ver}-{serial:03d}"
|
|
81
|
+
|
|
82
|
+
return True, f"Successfully deployed version {version_str}"
|
|
83
|
+
except Exception as e:
|
|
84
|
+
return False, f"Deployment failed: {str(e)}"
|
|
85
|
+
|
|
86
|
+
def check_deployment_needed(self, parent_path: Path) -> Tuple[bool, str]:
|
|
87
|
+
"""
|
|
88
|
+
Check if deployment is needed for a parent directory.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
parent_path: Path to parent directory
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Tuple of (needed, reason)
|
|
95
|
+
"""
|
|
96
|
+
target_file = parent_path / "CLAUDE.md"
|
|
97
|
+
|
|
98
|
+
if not target_file.exists():
|
|
99
|
+
return True, "CLAUDE.md does not exist"
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
with open(target_file, 'r') as f:
|
|
103
|
+
existing_content = f.read()
|
|
104
|
+
existing_fw_ver, _ = self.version_manager.parse_current_version(existing_content)
|
|
105
|
+
|
|
106
|
+
if existing_fw_ver != self.version_manager.framework_version:
|
|
107
|
+
return True, f"Version mismatch: {existing_fw_ver} vs {self.version_manager.framework_version}"
|
|
108
|
+
|
|
109
|
+
return False, "Already up to date"
|
|
110
|
+
except Exception as e:
|
|
111
|
+
return True, f"Error checking existing file: {str(e)}"
|
|
112
|
+
|
|
113
|
+
def backup_existing(self, parent_path: Path) -> Optional[Path]:
|
|
114
|
+
"""
|
|
115
|
+
Create a backup of existing CLAUDE.md before deployment.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
parent_path: Path to parent directory
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Path to backup file if created, None otherwise
|
|
122
|
+
"""
|
|
123
|
+
target_file = parent_path / "CLAUDE.md"
|
|
124
|
+
|
|
125
|
+
if not target_file.exists():
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
# Create backup filename with timestamp
|
|
129
|
+
timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
|
|
130
|
+
backup_file = parent_path / f"CLAUDE.md.backup.{timestamp}"
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
import shutil
|
|
134
|
+
shutil.copy2(target_file, backup_file)
|
|
135
|
+
return backup_file
|
|
136
|
+
except Exception:
|
|
137
|
+
return None
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Section generators for framework CLAUDE.md template.
|
|
3
|
+
|
|
4
|
+
This module provides base classes and registry for section generators.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, Any, Optional
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BaseSectionGenerator(ABC):
|
|
13
|
+
"""Base class for all section generators."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, framework_version: str):
|
|
16
|
+
"""
|
|
17
|
+
Initialize section generator.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
framework_version: Current framework version
|
|
21
|
+
"""
|
|
22
|
+
self.framework_version = framework_version
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def generate(self, data: Dict[str, Any]) -> str:
|
|
26
|
+
"""
|
|
27
|
+
Generate section content.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
data: Section-specific data
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
str: Generated section content
|
|
34
|
+
"""
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
def get_timestamp(self) -> str:
|
|
38
|
+
"""Get current UTC timestamp."""
|
|
39
|
+
return datetime.utcnow().isoformat()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class SectionGeneratorRegistry:
|
|
43
|
+
"""Registry for section generators."""
|
|
44
|
+
|
|
45
|
+
def __init__(self):
|
|
46
|
+
"""Initialize registry."""
|
|
47
|
+
self._generators = {}
|
|
48
|
+
|
|
49
|
+
def register(self, name: str, generator_class: type):
|
|
50
|
+
"""
|
|
51
|
+
Register a section generator.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
name: Section name
|
|
55
|
+
generator_class: Generator class
|
|
56
|
+
"""
|
|
57
|
+
self._generators[name] = generator_class
|
|
58
|
+
|
|
59
|
+
def get(self, name: str) -> Optional[type]:
|
|
60
|
+
"""
|
|
61
|
+
Get a section generator class.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
name: Section name
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Generator class or None
|
|
68
|
+
"""
|
|
69
|
+
return self._generators.get(name)
|
|
70
|
+
|
|
71
|
+
def list_sections(self) -> list:
|
|
72
|
+
"""Get list of registered section names."""
|
|
73
|
+
return list(self._generators.keys())
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# Global registry instance
|
|
77
|
+
section_registry = SectionGeneratorRegistry()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# Import and register all section generators
|
|
81
|
+
from .header import HeaderGenerator
|
|
82
|
+
from .role_designation import RoleDesignationGenerator
|
|
83
|
+
from .agents import AgentsGenerator
|
|
84
|
+
from .todo_task_tools import TodoTaskToolsGenerator
|
|
85
|
+
from .claude_pm_init import ClaudePmInitGenerator
|
|
86
|
+
from .orchestration_principles import OrchestrationPrinciplesGenerator
|
|
87
|
+
from .subprocess_validation import SubprocessValidationGenerator
|
|
88
|
+
from .delegation_constraints import DelegationConstraintsGenerator
|
|
89
|
+
from .environment_config import EnvironmentConfigGenerator
|
|
90
|
+
from .troubleshooting import TroubleshootingGenerator
|
|
91
|
+
from .core_responsibilities import CoreResponsibilitiesGenerator
|
|
92
|
+
from .footer import FooterGenerator
|
|
93
|
+
|
|
94
|
+
# Register all generators
|
|
95
|
+
section_registry.register('header', HeaderGenerator)
|
|
96
|
+
section_registry.register('role_designation', RoleDesignationGenerator)
|
|
97
|
+
section_registry.register('agents', AgentsGenerator)
|
|
98
|
+
section_registry.register('todo_task_tools', TodoTaskToolsGenerator)
|
|
99
|
+
section_registry.register('claude_pm_init', ClaudePmInitGenerator)
|
|
100
|
+
section_registry.register('orchestration_principles', OrchestrationPrinciplesGenerator)
|
|
101
|
+
section_registry.register('subprocess_validation', SubprocessValidationGenerator)
|
|
102
|
+
section_registry.register('delegation_constraints', DelegationConstraintsGenerator)
|
|
103
|
+
section_registry.register('environment_config', EnvironmentConfigGenerator)
|
|
104
|
+
section_registry.register('troubleshooting', TroubleshootingGenerator)
|
|
105
|
+
section_registry.register('core_responsibilities', CoreResponsibilitiesGenerator)
|
|
106
|
+
section_registry.register('footer', FooterGenerator)
|