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,596 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Agent Management Service
4
+ ========================
5
+
6
+ Comprehensive service for managing agent definitions with CRUD operations,
7
+ section extraction/updates, and version management.
8
+
9
+ Uses python-frontmatter and mistune for markdown parsing as recommended.
10
+ """
11
+
12
+ import os
13
+ import re
14
+ import json
15
+ import yaml
16
+ import logging
17
+ from pathlib import Path
18
+ from typing import Dict, List, Optional, Tuple, Any
19
+ from datetime import datetime
20
+
21
+ import frontmatter
22
+ import mistune
23
+
24
+ from ..models.agent_definition import (
25
+ AgentDefinition, AgentMetadata, AgentType,
26
+ AgentSection, AgentWorkflow, AgentPermissions
27
+ )
28
+ from .agent_versioning import AgentVersionManager
29
+ from .shared_prompt_cache import SharedPromptCache
30
+ from ..utils.paths import PathResolver
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ class AgentManager:
36
+ """Manages agent definitions with CRUD operations and versioning."""
37
+
38
+ def __init__(self, framework_dir: Optional[Path] = None, project_dir: Optional[Path] = None):
39
+ """
40
+ Initialize AgentManager.
41
+
42
+ Args:
43
+ framework_dir: Path to framework agent-roles directory
44
+ project_dir: Path to project-specific agents directory
45
+ """
46
+ # Use PathResolver for consistent path discovery
47
+ if framework_dir is None:
48
+ try:
49
+ framework_root = PathResolver.get_framework_root()
50
+ self.framework_dir = framework_root / "framework" / "agent-roles"
51
+ except FileNotFoundError:
52
+ # Fallback to agents directory
53
+ self.framework_dir = PathResolver.get_agents_dir()
54
+ else:
55
+ self.framework_dir = framework_dir
56
+
57
+ if project_dir is None:
58
+ project_root = PathResolver.get_project_root()
59
+ self.project_dir = project_root / ".claude-pm" / "agents" / "project-specific"
60
+ else:
61
+ self.project_dir = project_dir
62
+ self.version_manager = AgentVersionManager()
63
+ self.cache = SharedPromptCache.get_instance()
64
+ self._markdown = mistune.create_markdown()
65
+
66
+ def create_agent(self, name: str, definition: AgentDefinition, location: str = "project") -> Path:
67
+ """
68
+ Create a new agent definition file.
69
+
70
+ Args:
71
+ name: Agent name (e.g., "performance-agent")
72
+ definition: Agent definition object
73
+ location: "project" or "framework"
74
+
75
+ Returns:
76
+ Path to created file
77
+ """
78
+ # Determine target directory
79
+ target_dir = self.project_dir if location == "project" else self.framework_dir
80
+ target_dir.mkdir(parents=True, exist_ok=True)
81
+
82
+ # Generate markdown content
83
+ content = self._definition_to_markdown(definition)
84
+
85
+ # Write file
86
+ file_path = target_dir / f"{name}.md"
87
+ file_path.write_text(content, encoding='utf-8')
88
+
89
+ # Clear cache
90
+ self._clear_agent_cache(name)
91
+
92
+ logger.info(f"Created agent '{name}' at {file_path}")
93
+ return file_path
94
+
95
+ def read_agent(self, name: str) -> Optional[AgentDefinition]:
96
+ """
97
+ Read an agent definition.
98
+
99
+ Args:
100
+ name: Agent name (without .md extension)
101
+
102
+ Returns:
103
+ AgentDefinition or None if not found
104
+ """
105
+ # Try to find the agent file
106
+ agent_path = self._find_agent_file(name)
107
+ if not agent_path:
108
+ logger.warning(f"Agent '{name}' not found")
109
+ return None
110
+
111
+ try:
112
+ # Read and parse the file
113
+ content = agent_path.read_text(encoding='utf-8')
114
+ return self._parse_agent_markdown(content, name, str(agent_path))
115
+ except Exception as e:
116
+ logger.error(f"Error reading agent '{name}': {e}")
117
+ return None
118
+
119
+ def update_agent(self, name: str, updates: Dict[str, Any],
120
+ increment_version: bool = True) -> Optional[AgentDefinition]:
121
+ """
122
+ Update an agent definition.
123
+
124
+ Args:
125
+ name: Agent name
126
+ updates: Dictionary of updates to apply
127
+ increment_version: Whether to increment serial version
128
+
129
+ Returns:
130
+ Updated AgentDefinition or None if failed
131
+ """
132
+ # Read current definition
133
+ agent_def = self.read_agent(name)
134
+ if not agent_def:
135
+ return None
136
+
137
+ # Apply updates
138
+ for key, value in updates.items():
139
+ if hasattr(agent_def, key):
140
+ setattr(agent_def, key, value)
141
+ elif key in ["type", "model_preference", "tags", "specializations"]:
142
+ setattr(agent_def.metadata, key, value)
143
+
144
+ # Increment version if requested
145
+ if increment_version:
146
+ agent_def.metadata.increment_serial_version()
147
+ agent_def.metadata.last_updated = datetime.now()
148
+
149
+ # Write back
150
+ agent_path = self._find_agent_file(name)
151
+ if agent_path:
152
+ content = self._definition_to_markdown(agent_def)
153
+ agent_path.write_text(content, encoding='utf-8')
154
+
155
+ # Clear cache
156
+ self._clear_agent_cache(name)
157
+
158
+ logger.info(f"Updated agent '{name}' to version {agent_def.metadata.version}")
159
+ return agent_def
160
+
161
+ return None
162
+
163
+ def update_section(self, name: str, section: AgentSection, content: str,
164
+ increment_version: bool = True) -> Optional[AgentDefinition]:
165
+ """
166
+ Update a specific section of an agent.
167
+
168
+ Args:
169
+ name: Agent name
170
+ section: Section to update
171
+ content: New section content
172
+ increment_version: Whether to increment version
173
+
174
+ Returns:
175
+ Updated AgentDefinition or None
176
+ """
177
+ agent_def = self.read_agent(name)
178
+ if not agent_def:
179
+ return None
180
+
181
+ # Map section to attribute
182
+ section_map = {
183
+ AgentSection.PRIMARY_ROLE: "primary_role",
184
+ AgentSection.CAPABILITIES: "capabilities",
185
+ AgentSection.TOOLS: "tools_commands",
186
+ AgentSection.ESCALATION: "escalation_triggers",
187
+ AgentSection.KPI: "kpis",
188
+ AgentSection.DEPENDENCIES: "dependencies"
189
+ }
190
+
191
+ if section in section_map:
192
+ attr_name = section_map[section]
193
+ if section in [AgentSection.CAPABILITIES, AgentSection.ESCALATION,
194
+ AgentSection.KPI, AgentSection.DEPENDENCIES]:
195
+ # Parse list content
196
+ setattr(agent_def, attr_name, self._parse_list_content(content))
197
+ else:
198
+ setattr(agent_def, attr_name, content.strip())
199
+
200
+ # Special handling for complex sections
201
+ elif section == AgentSection.WHEN_TO_USE:
202
+ agent_def.when_to_use = self._parse_when_to_use(content)
203
+ elif section == AgentSection.AUTHORITY:
204
+ agent_def.authority = self._parse_authority(content)
205
+ elif section == AgentSection.WORKFLOWS:
206
+ agent_def.workflows = self._parse_workflows(content)
207
+
208
+ # Update raw section
209
+ agent_def.raw_sections[section.value] = content
210
+
211
+ # Increment version
212
+ if increment_version:
213
+ agent_def.metadata.increment_serial_version()
214
+ agent_def.metadata.last_updated = datetime.now()
215
+
216
+ # Write back
217
+ return self.update_agent(name, {}, increment_version=False)
218
+
219
+ def delete_agent(self, name: str) -> bool:
220
+ """
221
+ Delete an agent definition.
222
+
223
+ Args:
224
+ name: Agent name
225
+
226
+ Returns:
227
+ True if deleted, False otherwise
228
+ """
229
+ agent_path = self._find_agent_file(name)
230
+ if not agent_path:
231
+ return False
232
+
233
+ try:
234
+ agent_path.unlink()
235
+ self._clear_agent_cache(name)
236
+ logger.info(f"Deleted agent '{name}'")
237
+ return True
238
+ except Exception as e:
239
+ logger.error(f"Error deleting agent '{name}': {e}")
240
+ return False
241
+
242
+ def list_agents(self, location: Optional[str] = None) -> Dict[str, Dict[str, Any]]:
243
+ """
244
+ List all available agents.
245
+
246
+ Args:
247
+ location: Filter by location ("project", "framework", or None for all)
248
+
249
+ Returns:
250
+ Dictionary of agent info
251
+ """
252
+ agents = {}
253
+
254
+ # Check framework agents
255
+ if location in [None, "framework"]:
256
+ for agent_file in self.framework_dir.glob("*.md"):
257
+ if agent_file.name != "base_agent.md":
258
+ agent_name = agent_file.stem
259
+ agent_def = self.read_agent(agent_name)
260
+ if agent_def:
261
+ agents[agent_name] = {
262
+ "location": "framework",
263
+ "path": str(agent_file),
264
+ "version": agent_def.metadata.version,
265
+ "type": agent_def.metadata.type.value,
266
+ "specializations": agent_def.metadata.specializations
267
+ }
268
+
269
+ # Check project agents
270
+ if location in [None, "project"] and self.project_dir.exists():
271
+ for agent_file in self.project_dir.glob("*.md"):
272
+ agent_name = agent_file.stem
273
+ agent_def = self.read_agent(agent_name)
274
+ if agent_def:
275
+ agents[agent_name] = {
276
+ "location": "project",
277
+ "path": str(agent_file),
278
+ "version": agent_def.metadata.version,
279
+ "type": agent_def.metadata.type.value,
280
+ "specializations": agent_def.metadata.specializations
281
+ }
282
+
283
+ return agents
284
+
285
+ def get_agent_api(self, name: str) -> Optional[Dict[str, Any]]:
286
+ """
287
+ Get agent data in API-friendly format.
288
+
289
+ Args:
290
+ name: Agent name
291
+
292
+ Returns:
293
+ Agent data dictionary or None
294
+ """
295
+ agent_def = self.read_agent(name)
296
+ if not agent_def:
297
+ return None
298
+
299
+ return agent_def.to_dict()
300
+
301
+ # Private helper methods
302
+
303
+ def _find_agent_file(self, name: str) -> Optional[Path]:
304
+ """Find agent file in project or framework directories."""
305
+ # Check project first (higher precedence)
306
+ if self.project_dir.exists():
307
+ project_path = self.project_dir / f"{name}.md"
308
+ if project_path.exists():
309
+ return project_path
310
+
311
+ # Check framework
312
+ framework_path = self.framework_dir / f"{name}.md"
313
+ if framework_path.exists():
314
+ return framework_path
315
+
316
+ return None
317
+
318
+ def _parse_agent_markdown(self, content: str, name: str, file_path: str) -> AgentDefinition:
319
+ """Parse markdown content into AgentDefinition."""
320
+ # Parse frontmatter
321
+ post = frontmatter.loads(content)
322
+
323
+ # Extract metadata
324
+ metadata = AgentMetadata(
325
+ type=AgentType(post.metadata.get("type", "core")),
326
+ model_preference=post.metadata.get("model_preference", "claude-3-sonnet"),
327
+ version=post.metadata.get("version", "1.0.0"),
328
+ last_updated=post.metadata.get("last_updated"),
329
+ author=post.metadata.get("author"),
330
+ tags=post.metadata.get("tags", []),
331
+ specializations=post.metadata.get("specializations", [])
332
+ )
333
+
334
+ # Extract version from content if not in frontmatter
335
+ if not post.metadata.get("version"):
336
+ version = self.version_manager.extract_version_from_markdown(content)
337
+ if version:
338
+ metadata.version = version
339
+
340
+ # Parse sections
341
+ sections = self._extract_sections(post.content)
342
+
343
+ # Extract title
344
+ title_match = re.search(r'^#\s+(.+)$', post.content, re.MULTILINE)
345
+ title = title_match.group(1) if title_match else name.replace('-', ' ').title()
346
+
347
+ # Build definition
348
+ definition = AgentDefinition(
349
+ name=name,
350
+ title=title,
351
+ file_path=file_path,
352
+ metadata=metadata,
353
+ primary_role=sections.get("Primary Role", ""),
354
+ when_to_use=self._parse_when_to_use(sections.get("When to Use This Agent", "")),
355
+ capabilities=self._parse_list_content(sections.get("Core Capabilities", "")),
356
+ authority=self._parse_authority(sections.get("Authority & Permissions", "")),
357
+ workflows=self._parse_workflows(sections.get("Agent-Specific Workflows", "")),
358
+ escalation_triggers=self._parse_list_content(sections.get("Unique Escalation Triggers", "")),
359
+ kpis=self._parse_list_content(sections.get("Key Performance Indicators", "")),
360
+ dependencies=self._parse_list_content(sections.get("Critical Dependencies", "")),
361
+ tools_commands=sections.get("Specialized Tools/Commands", ""),
362
+ raw_content=content,
363
+ raw_sections=sections
364
+ )
365
+
366
+ return definition
367
+
368
+ def _extract_sections(self, content: str) -> Dict[str, str]:
369
+ """Extract sections from markdown content."""
370
+ sections = {}
371
+ current_section = None
372
+ current_content = []
373
+
374
+ # Split into lines
375
+ lines = content.split('\n')
376
+
377
+ for line in lines:
378
+ # Check if this is a section header
379
+ header_match = re.match(r'^##\s+(?:🎯|🔧|🔑|📋|🚨|📊|🔄|🛠️)?\s*(.+)$', line)
380
+ if header_match:
381
+ # Save previous section
382
+ if current_section:
383
+ sections[current_section] = '\n'.join(current_content).strip()
384
+
385
+ # Start new section
386
+ current_section = header_match.group(1).strip()
387
+ current_content = []
388
+ else:
389
+ # Add to current section
390
+ if current_section:
391
+ current_content.append(line)
392
+
393
+ # Save last section
394
+ if current_section:
395
+ sections[current_section] = '\n'.join(current_content).strip()
396
+
397
+ return sections
398
+
399
+ def _parse_list_content(self, content: str) -> List[str]:
400
+ """Parse bullet point or numbered list content."""
401
+ items = []
402
+ for line in content.split('\n'):
403
+ # Match bullet points or numbered items
404
+ match = re.match(r'^[-*•]\s+(.+)$|^\d+\.\s+(.+)$', line.strip())
405
+ if match:
406
+ item = match.group(1) or match.group(2)
407
+ items.append(item.strip())
408
+ return items
409
+
410
+ def _parse_when_to_use(self, content: str) -> Dict[str, List[str]]:
411
+ """Parse When to Use section."""
412
+ result = {"select": [], "do_not_select": []}
413
+ current_mode = None
414
+
415
+ for line in content.split('\n'):
416
+ if "Select this agent when:" in line or "**Select this agent when:**" in line:
417
+ current_mode = "select"
418
+ elif "Do NOT select for:" in line or "**Do NOT select for:**" in line:
419
+ current_mode = "do_not_select"
420
+ elif current_mode and line.strip().startswith('-'):
421
+ item = line.strip()[1:].strip()
422
+ result[current_mode].append(item)
423
+
424
+ return result
425
+
426
+ def _parse_authority(self, content: str) -> AgentPermissions:
427
+ """Parse Authority & Permissions section."""
428
+ permissions = AgentPermissions()
429
+ current_section = None
430
+
431
+ for line in content.split('\n'):
432
+ if "Exclusive Write Access" in line:
433
+ current_section = "write"
434
+ elif "Forbidden Operations" in line:
435
+ current_section = "forbidden"
436
+ elif "Read Access" in line:
437
+ current_section = "read"
438
+ elif current_section and line.strip().startswith('-'):
439
+ item = line.strip()[1:].strip()
440
+ # Remove inline comments
441
+ item = re.sub(r'\s*#.*$', '', item).strip()
442
+
443
+ if current_section == "write":
444
+ permissions.exclusive_write_access.append(item)
445
+ elif current_section == "forbidden":
446
+ permissions.forbidden_operations.append(item)
447
+ elif current_section == "read":
448
+ permissions.read_access.append(item)
449
+
450
+ return permissions
451
+
452
+ def _parse_workflows(self, content: str) -> List[AgentWorkflow]:
453
+ """Parse workflows from YAML blocks."""
454
+ workflows = []
455
+
456
+ # Find all YAML blocks
457
+ yaml_blocks = re.findall(r'```yaml\n(.*?)\n```', content, re.DOTALL)
458
+
459
+ for block in yaml_blocks:
460
+ try:
461
+ data = yaml.safe_load(block)
462
+ if isinstance(data, dict) and all(k in data for k in ["trigger", "process", "output"]):
463
+ # Extract workflow name from preceding heading if available
464
+ name_match = re.search(r'###\s+(.+)\n```yaml\n' + re.escape(block), content)
465
+ name = name_match.group(1) if name_match else "Unnamed Workflow"
466
+
467
+ workflow = AgentWorkflow(
468
+ name=name,
469
+ trigger=data["trigger"],
470
+ process=data["process"] if isinstance(data["process"], list) else [data["process"]],
471
+ output=data["output"],
472
+ raw_yaml=block
473
+ )
474
+ workflows.append(workflow)
475
+ except yaml.YAMLError:
476
+ logger.warning("Failed to parse YAML workflow block")
477
+
478
+ return workflows
479
+
480
+ def _definition_to_markdown(self, definition: AgentDefinition) -> str:
481
+ """Convert AgentDefinition back to markdown."""
482
+ # Start with frontmatter
483
+ frontmatter_data = {
484
+ "type": definition.metadata.type.value,
485
+ "model_preference": definition.metadata.model_preference,
486
+ "version": definition.metadata.version,
487
+ "last_updated": definition.metadata.last_updated,
488
+ "author": definition.metadata.author,
489
+ "tags": definition.metadata.tags,
490
+ "specializations": definition.metadata.specializations
491
+ }
492
+
493
+ # Remove None values
494
+ frontmatter_data = {k: v for k, v in frontmatter_data.items() if v is not None}
495
+
496
+ # Build content
497
+ content = []
498
+ content.append(f"# {definition.title}\n")
499
+
500
+ # Primary Role
501
+ content.append("## 🎯 Primary Role")
502
+ content.append(definition.primary_role)
503
+ content.append("")
504
+
505
+ # When to Use
506
+ content.append("## 🎯 When to Use This Agent")
507
+ content.append("")
508
+ content.append("**Select this agent when:**")
509
+ for item in definition.when_to_use.get("select", []):
510
+ content.append(f"- {item}")
511
+ content.append("")
512
+ content.append("**Do NOT select for:**")
513
+ for item in definition.when_to_use.get("do_not_select", []):
514
+ content.append(f"- {item}")
515
+ content.append("")
516
+
517
+ # Capabilities
518
+ content.append("## 🔧 Core Capabilities")
519
+ for capability in definition.capabilities:
520
+ content.append(f"- {capability}")
521
+ content.append("")
522
+
523
+ # Authority
524
+ content.append("## 🔑 Authority & Permissions")
525
+ content.append("")
526
+ content.append("### ✅ Exclusive Write Access")
527
+ for item in definition.authority.exclusive_write_access:
528
+ content.append(f"- {item}")
529
+ content.append("")
530
+ content.append("### ❌ Forbidden Operations")
531
+ for item in definition.authority.forbidden_operations:
532
+ content.append(f"- {item}")
533
+ content.append("")
534
+
535
+ # Workflows
536
+ if definition.workflows:
537
+ content.append("## 📋 Agent-Specific Workflows")
538
+ content.append("")
539
+ for workflow in definition.workflows:
540
+ content.append(f"### {workflow.name}")
541
+ content.append("```yaml")
542
+ yaml_content = {
543
+ "trigger": workflow.trigger,
544
+ "process": workflow.process,
545
+ "output": workflow.output
546
+ }
547
+ content.append(yaml.dump(yaml_content, default_flow_style=False).strip())
548
+ content.append("```")
549
+ content.append("")
550
+
551
+ # Escalation
552
+ if definition.escalation_triggers:
553
+ content.append("## 🚨 Unique Escalation Triggers")
554
+ for trigger in definition.escalation_triggers:
555
+ content.append(f"- {trigger}")
556
+ content.append("")
557
+
558
+ # KPIs
559
+ if definition.kpis:
560
+ content.append("## 📊 Key Performance Indicators")
561
+ for i, kpi in enumerate(definition.kpis, 1):
562
+ content.append(f"{i}. {kpi}")
563
+ content.append("")
564
+
565
+ # Dependencies
566
+ if definition.dependencies:
567
+ content.append("## 🔄 Critical Dependencies")
568
+ for dep in definition.dependencies:
569
+ content.append(f"- {dep}")
570
+ content.append("")
571
+
572
+ # Tools
573
+ if definition.tools_commands:
574
+ content.append("## 🛠️ Specialized Tools/Commands")
575
+ content.append(definition.tools_commands)
576
+ content.append("")
577
+
578
+ # Footer
579
+ content.append("---")
580
+ content.append(f"**Agent Type**: {definition.metadata.type.value}")
581
+ content.append(f"**Model Preference**: {definition.metadata.model_preference}")
582
+ content.append(f"**Version**: {definition.metadata.version}")
583
+ if definition.metadata.last_updated:
584
+ content.append(f"**Last Updated**: {definition.metadata.last_updated.strftime('%Y-%m-%d %H:%M:%S')}")
585
+
586
+ # Combine with frontmatter
587
+ post = frontmatter.Post('\n'.join(content), **frontmatter_data)
588
+ return frontmatter.dumps(post)
589
+
590
+ def _clear_agent_cache(self, name: str):
591
+ """Clear cache for a specific agent."""
592
+ try:
593
+ cache_key = f"agent_prompt:{name}:md"
594
+ self.cache.invalidate(cache_key)
595
+ except Exception as e:
596
+ logger.warning(f"Failed to clear cache for agent '{name}': {e}")