claude-mpm 4.13.2__py3-none-any.whl → 4.18.2__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 (250) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_ENGINEER.md +286 -0
  3. claude_mpm/agents/BASE_PM.md +48 -17
  4. claude_mpm/agents/OUTPUT_STYLE.md +329 -11
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +227 -8
  6. claude_mpm/agents/agent_loader.py +17 -5
  7. claude_mpm/agents/frontmatter_validator.py +284 -253
  8. claude_mpm/agents/templates/agentic-coder-optimizer.json +9 -2
  9. claude_mpm/agents/templates/api_qa.json +7 -1
  10. claude_mpm/agents/templates/clerk-ops.json +8 -1
  11. claude_mpm/agents/templates/code_analyzer.json +4 -1
  12. claude_mpm/agents/templates/dart_engineer.json +11 -1
  13. claude_mpm/agents/templates/data_engineer.json +11 -1
  14. claude_mpm/agents/templates/documentation.json +6 -1
  15. claude_mpm/agents/templates/engineer.json +18 -1
  16. claude_mpm/agents/templates/gcp_ops_agent.json +8 -1
  17. claude_mpm/agents/templates/golang_engineer.json +11 -1
  18. claude_mpm/agents/templates/java_engineer.json +12 -2
  19. claude_mpm/agents/templates/local_ops_agent.json +1217 -6
  20. claude_mpm/agents/templates/nextjs_engineer.json +11 -1
  21. claude_mpm/agents/templates/ops.json +8 -1
  22. claude_mpm/agents/templates/php-engineer.json +11 -1
  23. claude_mpm/agents/templates/project_organizer.json +10 -3
  24. claude_mpm/agents/templates/prompt-engineer.json +5 -1
  25. claude_mpm/agents/templates/python_engineer.json +11 -1
  26. claude_mpm/agents/templates/qa.json +7 -1
  27. claude_mpm/agents/templates/react_engineer.json +11 -1
  28. claude_mpm/agents/templates/refactoring_engineer.json +8 -1
  29. claude_mpm/agents/templates/research.json +4 -1
  30. claude_mpm/agents/templates/ruby-engineer.json +11 -1
  31. claude_mpm/agents/templates/rust_engineer.json +11 -1
  32. claude_mpm/agents/templates/security.json +6 -1
  33. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  34. claude_mpm/agents/templates/ticketing.json +6 -1
  35. claude_mpm/agents/templates/typescript_engineer.json +11 -1
  36. claude_mpm/agents/templates/vercel_ops_agent.json +8 -1
  37. claude_mpm/agents/templates/version_control.json +8 -1
  38. claude_mpm/agents/templates/web_qa.json +7 -1
  39. claude_mpm/agents/templates/web_ui.json +11 -1
  40. claude_mpm/cli/__init__.py +34 -706
  41. claude_mpm/cli/commands/agent_manager.py +25 -12
  42. claude_mpm/cli/commands/agent_state_manager.py +186 -0
  43. claude_mpm/cli/commands/agents.py +204 -148
  44. claude_mpm/cli/commands/aggregate.py +7 -3
  45. claude_mpm/cli/commands/analyze.py +9 -4
  46. claude_mpm/cli/commands/analyze_code.py +7 -2
  47. claude_mpm/cli/commands/auto_configure.py +7 -9
  48. claude_mpm/cli/commands/config.py +47 -13
  49. claude_mpm/cli/commands/configure.py +294 -1788
  50. claude_mpm/cli/commands/configure_agent_display.py +261 -0
  51. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  52. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  53. claude_mpm/cli/commands/configure_models.py +18 -0
  54. claude_mpm/cli/commands/configure_navigation.py +167 -0
  55. claude_mpm/cli/commands/configure_paths.py +104 -0
  56. claude_mpm/cli/commands/configure_persistence.py +254 -0
  57. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  58. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  59. claude_mpm/cli/commands/configure_validators.py +73 -0
  60. claude_mpm/cli/commands/local_deploy.py +537 -0
  61. claude_mpm/cli/commands/memory.py +54 -20
  62. claude_mpm/cli/commands/mpm_init.py +39 -25
  63. claude_mpm/cli/commands/mpm_init_handler.py +8 -3
  64. claude_mpm/cli/executor.py +202 -0
  65. claude_mpm/cli/helpers.py +105 -0
  66. claude_mpm/cli/interactive/__init__.py +3 -0
  67. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  68. claude_mpm/cli/parsers/__init__.py +7 -1
  69. claude_mpm/cli/parsers/base_parser.py +98 -3
  70. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  71. claude_mpm/cli/shared/output_formatters.py +28 -19
  72. claude_mpm/cli/startup.py +481 -0
  73. claude_mpm/cli/utils.py +52 -1
  74. claude_mpm/commands/mpm-help.md +3 -0
  75. claude_mpm/commands/mpm-version.md +113 -0
  76. claude_mpm/commands/mpm.md +1 -0
  77. claude_mpm/config/agent_config.py +2 -2
  78. claude_mpm/config/model_config.py +428 -0
  79. claude_mpm/core/base_service.py +13 -12
  80. claude_mpm/core/enums.py +452 -0
  81. claude_mpm/core/factories.py +1 -1
  82. claude_mpm/core/instruction_reinforcement_hook.py +2 -1
  83. claude_mpm/core/interactive_session.py +9 -3
  84. claude_mpm/core/logging_config.py +6 -2
  85. claude_mpm/core/oneshot_session.py +8 -4
  86. claude_mpm/core/optimized_agent_loader.py +3 -3
  87. claude_mpm/core/output_style_manager.py +12 -192
  88. claude_mpm/core/service_registry.py +5 -1
  89. claude_mpm/core/types.py +2 -9
  90. claude_mpm/core/typing_utils.py +7 -6
  91. claude_mpm/dashboard/static/js/dashboard.js +0 -14
  92. claude_mpm/dashboard/templates/index.html +3 -41
  93. claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
  94. claude_mpm/hooks/instruction_reinforcement.py +7 -2
  95. claude_mpm/models/resume_log.py +340 -0
  96. claude_mpm/services/agents/auto_config_manager.py +10 -11
  97. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  98. claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
  99. claude_mpm/services/agents/deployment/agent_validator.py +17 -1
  100. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  101. claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
  102. claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
  103. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
  104. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  105. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  106. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
  107. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  108. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
  109. claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
  110. claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
  111. claude_mpm/services/agents/local_template_manager.py +1 -1
  112. claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
  113. claude_mpm/services/agents/registry/modification_tracker.py +5 -2
  114. claude_mpm/services/command_handler_service.py +11 -5
  115. claude_mpm/services/core/interfaces/__init__.py +74 -2
  116. claude_mpm/services/core/interfaces/health.py +172 -0
  117. claude_mpm/services/core/interfaces/model.py +281 -0
  118. claude_mpm/services/core/interfaces/process.py +372 -0
  119. claude_mpm/services/core/interfaces/restart.py +307 -0
  120. claude_mpm/services/core/interfaces/stability.py +260 -0
  121. claude_mpm/services/core/models/__init__.py +33 -0
  122. claude_mpm/services/core/models/agent_config.py +12 -28
  123. claude_mpm/services/core/models/health.py +162 -0
  124. claude_mpm/services/core/models/process.py +235 -0
  125. claude_mpm/services/core/models/restart.py +302 -0
  126. claude_mpm/services/core/models/stability.py +264 -0
  127. claude_mpm/services/core/path_resolver.py +23 -7
  128. claude_mpm/services/diagnostics/__init__.py +2 -2
  129. claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
  130. claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
  131. claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
  132. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
  133. claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
  134. claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
  135. claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
  136. claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
  137. claude_mpm/services/diagnostics/checks/mcp_services_check.py +36 -31
  138. claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
  139. claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
  140. claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
  141. claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
  142. claude_mpm/services/diagnostics/models.py +19 -24
  143. claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
  144. claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
  145. claude_mpm/services/infrastructure/monitoring/base.py +5 -13
  146. claude_mpm/services/infrastructure/monitoring/network.py +7 -6
  147. claude_mpm/services/infrastructure/monitoring/process.py +13 -12
  148. claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
  149. claude_mpm/services/infrastructure/monitoring/service.py +16 -15
  150. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  151. claude_mpm/services/local_ops/__init__.py +163 -0
  152. claude_mpm/services/local_ops/crash_detector.py +257 -0
  153. claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
  154. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  155. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  156. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  157. claude_mpm/services/local_ops/health_manager.py +430 -0
  158. claude_mpm/services/local_ops/log_monitor.py +396 -0
  159. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  160. claude_mpm/services/local_ops/process_manager.py +595 -0
  161. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  162. claude_mpm/services/local_ops/restart_manager.py +401 -0
  163. claude_mpm/services/local_ops/restart_policy.py +387 -0
  164. claude_mpm/services/local_ops/state_manager.py +372 -0
  165. claude_mpm/services/local_ops/unified_manager.py +600 -0
  166. claude_mpm/services/mcp_config_manager.py +9 -4
  167. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  168. claude_mpm/services/mcp_gateway/core/base.py +18 -31
  169. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +71 -24
  170. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
  171. claude_mpm/services/memory_hook_service.py +4 -1
  172. claude_mpm/services/model/__init__.py +147 -0
  173. claude_mpm/services/model/base_provider.py +365 -0
  174. claude_mpm/services/model/claude_provider.py +412 -0
  175. claude_mpm/services/model/model_router.py +453 -0
  176. claude_mpm/services/model/ollama_provider.py +415 -0
  177. claude_mpm/services/monitor/daemon_manager.py +3 -2
  178. claude_mpm/services/monitor/handlers/dashboard.py +2 -1
  179. claude_mpm/services/monitor/handlers/hooks.py +2 -1
  180. claude_mpm/services/monitor/management/lifecycle.py +3 -2
  181. claude_mpm/services/monitor/server.py +2 -1
  182. claude_mpm/services/session_management_service.py +3 -2
  183. claude_mpm/services/session_manager.py +205 -1
  184. claude_mpm/services/shared/async_service_base.py +16 -27
  185. claude_mpm/services/shared/lifecycle_service_base.py +1 -14
  186. claude_mpm/services/socketio/handlers/__init__.py +5 -2
  187. claude_mpm/services/socketio/handlers/hook.py +13 -2
  188. claude_mpm/services/socketio/handlers/registry.py +4 -2
  189. claude_mpm/services/socketio/server/main.py +10 -8
  190. claude_mpm/services/subprocess_launcher_service.py +14 -5
  191. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
  192. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
  193. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
  194. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
  195. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
  196. claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
  197. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
  198. claude_mpm/services/unified/deployment_strategies/local.py +6 -5
  199. claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
  200. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
  201. claude_mpm/services/unified/interfaces.py +3 -1
  202. claude_mpm/services/unified/unified_analyzer.py +14 -10
  203. claude_mpm/services/unified/unified_config.py +2 -1
  204. claude_mpm/services/unified/unified_deployment.py +9 -4
  205. claude_mpm/services/version_service.py +104 -1
  206. claude_mpm/skills/__init__.py +21 -0
  207. claude_mpm/skills/bundled/__init__.py +6 -0
  208. claude_mpm/skills/bundled/api-documentation.md +393 -0
  209. claude_mpm/skills/bundled/async-testing.md +571 -0
  210. claude_mpm/skills/bundled/code-review.md +143 -0
  211. claude_mpm/skills/bundled/database-migration.md +199 -0
  212. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  213. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  214. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  215. claude_mpm/skills/bundled/git-workflow.md +414 -0
  216. claude_mpm/skills/bundled/imagemagick.md +204 -0
  217. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  218. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  219. claude_mpm/skills/bundled/pdf.md +141 -0
  220. claude_mpm/skills/bundled/performance-profiling.md +567 -0
  221. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  222. claude_mpm/skills/bundled/security-scanning.md +327 -0
  223. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  224. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  225. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  226. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  227. claude_mpm/skills/bundled/xlsx.md +157 -0
  228. claude_mpm/skills/registry.py +286 -0
  229. claude_mpm/skills/skill_manager.py +310 -0
  230. claude_mpm/tools/code_tree_analyzer.py +177 -141
  231. claude_mpm/tools/code_tree_events.py +4 -2
  232. claude_mpm/utils/agent_dependency_loader.py +2 -2
  233. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +117 -8
  234. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +238 -174
  235. claude_mpm/dashboard/static/css/code-tree.css +0 -1639
  236. claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
  237. claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
  238. claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
  239. claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
  240. claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
  241. claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
  242. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
  243. claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
  244. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
  245. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
  246. claude_mpm/services/project/analyzer_refactored.py +0 -450
  247. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
  248. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
  249. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
  250. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,157 @@
1
+ ---
2
+ skill_id: xlsx
3
+ skill_version: 0.1.0
4
+ description: Working with Excel files programmatically.
5
+ updated_at: 2025-10-30T17:00:00Z
6
+ tags: [excel, xlsx, spreadsheet, data]
7
+ ---
8
+
9
+ # Excel/XLSX Manipulation
10
+
11
+ Working with Excel files programmatically.
12
+
13
+ ## Python (openpyxl)
14
+
15
+ ### Reading Excel
16
+ ```python
17
+ from openpyxl import load_workbook
18
+
19
+ wb = load_workbook('data.xlsx')
20
+ ws = wb.active # Get active sheet
21
+
22
+ # Read cell
23
+ value = ws['A1'].value
24
+
25
+ # Iterate rows
26
+ for row in ws.iter_rows(min_row=2, values_only=True):
27
+ print(row)
28
+ ```
29
+
30
+ ### Writing Excel
31
+ ```python
32
+ from openpyxl import Workbook
33
+
34
+ wb = Workbook()
35
+ ws = wb.active
36
+ ws.title = "Data"
37
+
38
+ # Write data
39
+ ws['A1'] = 'Name'
40
+ ws['B1'] = 'Age'
41
+ ws.append(['John', 30])
42
+ ws.append(['Jane', 25])
43
+
44
+ wb.save('output.xlsx')
45
+ ```
46
+
47
+ ### Formatting
48
+ ```python
49
+ from openpyxl.styles import Font, PatternFill
50
+
51
+ # Bold header
52
+ ws['A1'].font = Font(bold=True)
53
+
54
+ # Background color
55
+ ws['A1'].fill = PatternFill(start_color="FFFF00", fill_type="solid")
56
+
57
+ # Number format
58
+ ws['B2'].number_format = '0.00' # Two decimals
59
+ ```
60
+
61
+ ### Formulas
62
+ ```python
63
+ # Add formula
64
+ ws['C2'] = '=A2+B2'
65
+
66
+ # Sum column
67
+ ws['D10'] = '=SUM(D2:D9)'
68
+ ```
69
+
70
+ ## Python (pandas)
71
+
72
+ ### Reading Excel
73
+ ```python
74
+ import pandas as pd
75
+
76
+ # Read sheet
77
+ df = pd.read_excel('data.xlsx', sheet_name='Sheet1')
78
+
79
+ # Read multiple sheets
80
+ dfs = pd.read_excel('data.xlsx', sheet_name=None)
81
+ ```
82
+
83
+ ### Writing Excel
84
+ ```python
85
+ # Write DataFrame
86
+ df.to_excel('output.xlsx', index=False)
87
+
88
+ # Multiple sheets
89
+ with pd.ExcelWriter('output.xlsx') as writer:
90
+ df1.to_excel(writer, sheet_name='Sheet1')
91
+ df2.to_excel(writer, sheet_name='Sheet2')
92
+ ```
93
+
94
+ ### Data Transformation
95
+ ```python
96
+ # Filter
97
+ filtered = df[df['Age'] > 25]
98
+
99
+ # Group by
100
+ grouped = df.groupby('Department')['Salary'].mean()
101
+
102
+ # Pivot
103
+ pivot = df.pivot_table(values='Sales', index='Region', columns='Product')
104
+ ```
105
+
106
+ ## JavaScript (xlsx)
107
+
108
+ ```javascript
109
+ import XLSX from 'xlsx';
110
+
111
+ // Read file
112
+ const workbook = XLSX.readFile('data.xlsx');
113
+ const sheetName = workbook.SheetNames[0];
114
+ const worksheet = workbook.Sheets[sheetName];
115
+
116
+ // Convert to JSON
117
+ const data = XLSX.utils.sheet_to_json(worksheet);
118
+
119
+ // Write file
120
+ const newWorksheet = XLSX.utils.json_to_sheet(data);
121
+ const newWorkbook = XLSX.utils.book_new();
122
+ XLSX.utils.book_append_sheet(newWorkbook, newWorksheet, 'Data');
123
+ XLSX.writeFile(newWorkbook, 'output.xlsx');
124
+ ```
125
+
126
+ ## Common Operations
127
+
128
+ ### CSV to Excel
129
+ ```python
130
+ import pandas as pd
131
+
132
+ df = pd.read_csv('data.csv')
133
+ df.to_excel('data.xlsx', index=False)
134
+ ```
135
+
136
+ ### Excel to CSV
137
+ ```python
138
+ df = pd.read_excel('data.xlsx')
139
+ df.to_csv('data.csv', index=False)
140
+ ```
141
+
142
+ ### Merging Excel Files
143
+ ```python
144
+ dfs = []
145
+ for file in ['file1.xlsx', 'file2.xlsx', 'file3.xlsx']:
146
+ df = pd.read_excel(file)
147
+ dfs.append(df)
148
+
149
+ combined = pd.concat(dfs, ignore_index=True)
150
+ combined.to_excel('merged.xlsx', index=False)
151
+ ```
152
+
153
+ ## Remember
154
+ - Close workbooks after use
155
+ - Handle large files in chunks
156
+ - Validate data before writing
157
+ - Use pandas for data analysis, openpyxl for formatting
@@ -0,0 +1,286 @@
1
+ """Skills registry - manages bundled and discovered skills."""
2
+
3
+ import re
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+ from typing import Any, Dict, List, Optional
7
+
8
+ import yaml
9
+
10
+ from claude_mpm.core.logging_utils import get_logger
11
+
12
+ logger = get_logger(__name__)
13
+
14
+
15
+ @dataclass
16
+ class Skill:
17
+ """Represents a skill that can be used by agents."""
18
+
19
+ name: str
20
+ path: Path
21
+ content: str
22
+ source: str # 'bundled', 'user', or 'project'
23
+
24
+ # Version tracking fields
25
+ version: str = "0.1.0"
26
+ skill_id: str = "" # defaults to name if not provided
27
+
28
+ # Existing fields
29
+ description: str = ""
30
+ agent_types: List[str] = None # Which agent types can use this skill
31
+
32
+ # Optional metadata
33
+ updated_at: Optional[str] = None
34
+ tags: List[str] = None
35
+
36
+ def __post_init__(self):
37
+ """Initialize default values if not provided."""
38
+ if self.agent_types is None:
39
+ self.agent_types = []
40
+ if self.tags is None:
41
+ self.tags = []
42
+ if not self.skill_id:
43
+ self.skill_id = self.name
44
+
45
+
46
+ class SkillsRegistry:
47
+ """Registry for managing skills across all tiers."""
48
+
49
+ def __init__(self):
50
+ """Initialize the skills registry."""
51
+ self.skills: Dict[str, Skill] = {}
52
+ self._load_bundled_skills()
53
+ self._load_user_skills()
54
+ self._load_project_skills()
55
+
56
+ def _parse_skill_frontmatter(self, content: str) -> Dict[str, Any]:
57
+ """Parse YAML frontmatter from skill markdown file.
58
+
59
+ Returns:
60
+ Dict with frontmatter fields or empty dict if no frontmatter
61
+ """
62
+ # Check for YAML frontmatter
63
+ if not content.startswith("---"):
64
+ return {}
65
+
66
+ # Extract frontmatter (match: ---\n...yaml...\n---\nrest)
67
+ match = re.match(r"^---\n(.*?)\n---\n(.*)$", content, re.DOTALL)
68
+ if not match:
69
+ return {}
70
+
71
+ try:
72
+ frontmatter = yaml.safe_load(match.group(1))
73
+ return frontmatter or {}
74
+ except yaml.YAMLError as e:
75
+ logger.warning(f"Failed to parse skill frontmatter: {e}")
76
+ return {}
77
+
78
+ def _load_bundled_skills(self):
79
+ """Load skills bundled with MPM."""
80
+ bundled_dir = Path(__file__).parent / "bundled"
81
+ if not bundled_dir.exists():
82
+ logger.warning(f"Bundled skills directory not found: {bundled_dir}")
83
+ return
84
+
85
+ skill_count = 0
86
+ for skill_file in bundled_dir.glob("*.md"):
87
+ try:
88
+ skill_name = skill_file.stem
89
+ content = skill_file.read_text(encoding="utf-8")
90
+
91
+ # Parse frontmatter
92
+ frontmatter = self._parse_skill_frontmatter(content)
93
+
94
+ # Extract version fields from frontmatter
95
+ version = frontmatter.get("skill_version", "0.1.0")
96
+ skill_id = frontmatter.get("skill_id", skill_name)
97
+ updated_at = frontmatter.get("updated_at")
98
+ tags = frontmatter.get("tags", [])
99
+
100
+ # Extract description (from frontmatter or fallback to content parsing)
101
+ description = frontmatter.get("description", "")
102
+ if not description:
103
+ description = self._extract_description(content)
104
+
105
+ self.skills[skill_name] = Skill(
106
+ name=skill_name,
107
+ path=skill_file,
108
+ content=content,
109
+ source="bundled",
110
+ version=version,
111
+ skill_id=skill_id,
112
+ description=description,
113
+ updated_at=updated_at,
114
+ tags=tags,
115
+ )
116
+ skill_count += 1
117
+ except Exception as e:
118
+ logger.error(f"Error loading bundled skill {skill_file}: {e}")
119
+
120
+ logger.debug(f"Loaded {skill_count} bundled skills")
121
+
122
+ def _load_user_skills(self):
123
+ """Load user-installed skills from ~/.claude/skills/"""
124
+ user_skills_dir = Path.home() / ".claude" / "skills"
125
+ if not user_skills_dir.exists():
126
+ logger.debug("User skills directory not found, skipping")
127
+ return
128
+
129
+ skill_count = 0
130
+ for skill_file in user_skills_dir.glob("*.md"):
131
+ try:
132
+ skill_name = skill_file.stem
133
+ # User skills override bundled skills
134
+ content = skill_file.read_text(encoding="utf-8")
135
+
136
+ # Parse frontmatter
137
+ frontmatter = self._parse_skill_frontmatter(content)
138
+
139
+ # Extract version fields from frontmatter
140
+ version = frontmatter.get("skill_version", "0.1.0")
141
+ skill_id = frontmatter.get("skill_id", skill_name)
142
+ updated_at = frontmatter.get("updated_at")
143
+ tags = frontmatter.get("tags", [])
144
+
145
+ # Extract description (from frontmatter or fallback to content parsing)
146
+ description = frontmatter.get("description", "")
147
+ if not description:
148
+ description = self._extract_description(content)
149
+
150
+ self.skills[skill_name] = Skill(
151
+ name=skill_name,
152
+ path=skill_file,
153
+ content=content,
154
+ source="user",
155
+ version=version,
156
+ skill_id=skill_id,
157
+ description=description,
158
+ updated_at=updated_at,
159
+ tags=tags,
160
+ )
161
+ skill_count += 1
162
+ logger.debug(f"User skill '{skill_name}' overrides bundled version")
163
+ except Exception as e:
164
+ logger.error(f"Error loading user skill {skill_file}: {e}")
165
+
166
+ if skill_count > 0:
167
+ logger.debug(f"Loaded {skill_count} user skills")
168
+
169
+ def _load_project_skills(self):
170
+ """Load project-specific skills from .claude/skills/"""
171
+ project_skills_dir = Path.cwd() / ".claude" / "skills"
172
+ if not project_skills_dir.exists():
173
+ logger.debug("Project skills directory not found, skipping")
174
+ return
175
+
176
+ skill_count = 0
177
+ for skill_file in project_skills_dir.glob("*.md"):
178
+ try:
179
+ skill_name = skill_file.stem
180
+ # Project skills override both user and bundled skills
181
+ content = skill_file.read_text(encoding="utf-8")
182
+
183
+ # Parse frontmatter
184
+ frontmatter = self._parse_skill_frontmatter(content)
185
+
186
+ # Extract version fields from frontmatter
187
+ version = frontmatter.get("skill_version", "0.1.0")
188
+ skill_id = frontmatter.get("skill_id", skill_name)
189
+ updated_at = frontmatter.get("updated_at")
190
+ tags = frontmatter.get("tags", [])
191
+
192
+ # Extract description (from frontmatter or fallback to content parsing)
193
+ description = frontmatter.get("description", "")
194
+ if not description:
195
+ description = self._extract_description(content)
196
+
197
+ self.skills[skill_name] = Skill(
198
+ name=skill_name,
199
+ path=skill_file,
200
+ content=content,
201
+ source="project",
202
+ version=version,
203
+ skill_id=skill_id,
204
+ description=description,
205
+ updated_at=updated_at,
206
+ tags=tags,
207
+ )
208
+ skill_count += 1
209
+ logger.debug(f"Project skill '{skill_name}' overrides other versions")
210
+ except Exception as e:
211
+ logger.error(f"Error loading project skill {skill_file}: {e}")
212
+
213
+ if skill_count > 0:
214
+ logger.debug(f"Loaded {skill_count} project skills")
215
+
216
+ def _extract_description(self, content: str) -> str:
217
+ """Extract description from skill content (first paragraph or summary)."""
218
+ lines = content.strip().split("\n")
219
+ description_lines = []
220
+
221
+ # Skip title (first line starting with #)
222
+ start_idx = 0
223
+ if lines and lines[0].startswith("#"):
224
+ start_idx = 1
225
+
226
+ # Find first non-empty paragraph
227
+ for line in lines[start_idx:]:
228
+ line = line.strip()
229
+ if not line:
230
+ if description_lines:
231
+ break
232
+ continue
233
+ if line.startswith("#"):
234
+ break
235
+ description_lines.append(line)
236
+
237
+ return " ".join(description_lines)[:200] # Limit to 200 chars
238
+
239
+ def get_skill(self, name: str) -> Optional[Skill]:
240
+ """Get a skill by name."""
241
+ return self.skills.get(name)
242
+
243
+ def list_skills(self, source: Optional[str] = None) -> List[Skill]:
244
+ """List all skills, optionally filtered by source."""
245
+ if source:
246
+ return [s for s in self.skills.values() if s.source == source]
247
+ return list(self.skills.values())
248
+
249
+ def get_skills_for_agent(self, agent_type: str) -> List[Skill]:
250
+ """
251
+ Get skills mapped to a specific agent type.
252
+
253
+ Args:
254
+ agent_type: Agent type/ID (e.g., 'engineer', 'python_engineer')
255
+
256
+ Returns:
257
+ List of skills applicable to this agent type
258
+ """
259
+ # Filter skills that explicitly list this agent type
260
+ # If a skill has no agent_types specified, it's available to all agents
261
+ return [
262
+ skill
263
+ for skill in self.skills.values()
264
+ if not skill.agent_types or agent_type in skill.agent_types
265
+ ]
266
+
267
+ def reload(self):
268
+ """Reload all skills from disk."""
269
+ logger.info("Reloading skills registry...")
270
+ self.skills.clear()
271
+ self._load_bundled_skills()
272
+ self._load_user_skills()
273
+ self._load_project_skills()
274
+ logger.info(f"Skills registry reloaded with {len(self.skills)} skills")
275
+
276
+
277
+ # Global registry instance (singleton pattern)
278
+ _registry: Optional[SkillsRegistry] = None
279
+
280
+
281
+ def get_registry() -> SkillsRegistry:
282
+ """Get the global skills registry (singleton)."""
283
+ global _registry
284
+ if _registry is None:
285
+ _registry = SkillsRegistry()
286
+ return _registry