claude-mpm 5.0.2__py3-none-any.whl → 5.4.3__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 (184) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
  3. claude_mpm/agents/PM_INSTRUCTIONS.md +1218 -905
  4. claude_mpm/agents/agent_loader.py +10 -17
  5. claude_mpm/agents/base_agent_loader.py +10 -35
  6. claude_mpm/agents/frontmatter_validator.py +68 -0
  7. claude_mpm/agents/templates/circuit-breakers.md +431 -45
  8. claude_mpm/cli/__init__.py +0 -1
  9. claude_mpm/cli/commands/__init__.py +2 -0
  10. claude_mpm/cli/commands/agent_state_manager.py +67 -23
  11. claude_mpm/cli/commands/agents.py +446 -25
  12. claude_mpm/cli/commands/auto_configure.py +535 -233
  13. claude_mpm/cli/commands/configure.py +1500 -147
  14. claude_mpm/cli/commands/configure_agent_display.py +13 -6
  15. claude_mpm/cli/commands/mpm_init/core.py +158 -1
  16. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  17. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  18. claude_mpm/cli/commands/postmortem.py +401 -0
  19. claude_mpm/cli/commands/run.py +1 -39
  20. claude_mpm/cli/commands/skills.py +322 -19
  21. claude_mpm/cli/commands/summarize.py +413 -0
  22. claude_mpm/cli/executor.py +8 -0
  23. claude_mpm/cli/interactive/agent_wizard.py +302 -195
  24. claude_mpm/cli/parsers/agents_parser.py +137 -0
  25. claude_mpm/cli/parsers/auto_configure_parser.py +13 -0
  26. claude_mpm/cli/parsers/base_parser.py +9 -0
  27. claude_mpm/cli/parsers/skills_parser.py +7 -0
  28. claude_mpm/cli/startup.py +133 -85
  29. claude_mpm/commands/mpm-agents-auto-configure.md +2 -2
  30. claude_mpm/commands/mpm-agents-list.md +2 -2
  31. claude_mpm/commands/mpm-config-view.md +2 -2
  32. claude_mpm/commands/mpm-help.md +3 -0
  33. claude_mpm/commands/{mpm-ticket-organize.md → mpm-organize.md} +4 -5
  34. claude_mpm/commands/mpm-postmortem.md +123 -0
  35. claude_mpm/commands/mpm-session-resume.md +2 -2
  36. claude_mpm/commands/mpm-ticket-view.md +2 -2
  37. claude_mpm/config/agent_presets.py +312 -82
  38. claude_mpm/config/agent_sources.py +27 -0
  39. claude_mpm/config/skill_presets.py +392 -0
  40. claude_mpm/constants.py +1 -0
  41. claude_mpm/core/claude_runner.py +2 -25
  42. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  43. claude_mpm/core/framework/loaders/file_loader.py +54 -101
  44. claude_mpm/core/interactive_session.py +19 -5
  45. claude_mpm/core/oneshot_session.py +16 -4
  46. claude_mpm/core/output_style_manager.py +173 -43
  47. claude_mpm/core/protocols/__init__.py +23 -0
  48. claude_mpm/core/protocols/runner_protocol.py +103 -0
  49. claude_mpm/core/protocols/session_protocol.py +131 -0
  50. claude_mpm/core/shared/singleton_manager.py +11 -4
  51. claude_mpm/core/socketio_pool.py +3 -3
  52. claude_mpm/core/system_context.py +38 -0
  53. claude_mpm/core/unified_agent_registry.py +134 -16
  54. claude_mpm/core/unified_config.py +22 -0
  55. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  56. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-313.pyc +0 -0
  57. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  58. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  59. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  60. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  61. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  62. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  63. claude_mpm/hooks/claude_hooks/event_handlers.py +35 -2
  64. claude_mpm/hooks/claude_hooks/hook_handler.py +4 -0
  65. claude_mpm/hooks/claude_hooks/memory_integration.py +12 -1
  66. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  67. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  68. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  69. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  70. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  71. claude_mpm/hooks/claude_hooks/services/connection_manager.py +4 -0
  72. claude_mpm/models/agent_definition.py +7 -0
  73. claude_mpm/scripts/launch_monitor.py +93 -13
  74. claude_mpm/services/agents/agent_recommendation_service.py +279 -0
  75. claude_mpm/services/agents/cache_git_manager.py +621 -0
  76. claude_mpm/services/agents/deployment/agent_template_builder.py +3 -2
  77. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +110 -3
  78. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +518 -55
  79. claude_mpm/services/agents/git_source_manager.py +20 -0
  80. claude_mpm/services/agents/sources/git_source_sync_service.py +45 -6
  81. claude_mpm/services/agents/toolchain_detector.py +6 -5
  82. claude_mpm/services/analysis/__init__.py +35 -0
  83. claude_mpm/services/analysis/clone_detector.py +1030 -0
  84. claude_mpm/services/analysis/postmortem_reporter.py +474 -0
  85. claude_mpm/services/analysis/postmortem_service.py +765 -0
  86. claude_mpm/services/command_deployment_service.py +106 -5
  87. claude_mpm/services/core/base.py +7 -2
  88. claude_mpm/services/diagnostics/checks/mcp_services_check.py +7 -15
  89. claude_mpm/services/event_bus/config.py +3 -1
  90. claude_mpm/services/git/git_operations_service.py +8 -8
  91. claude_mpm/services/mcp_config_manager.py +75 -145
  92. claude_mpm/services/mcp_service_verifier.py +6 -3
  93. claude_mpm/services/monitor/daemon.py +37 -10
  94. claude_mpm/services/monitor/daemon_manager.py +134 -21
  95. claude_mpm/services/monitor/server.py +225 -19
  96. claude_mpm/services/project/project_organizer.py +4 -0
  97. claude_mpm/services/runner_configuration_service.py +16 -3
  98. claude_mpm/services/session_management_service.py +16 -4
  99. claude_mpm/services/socketio/event_normalizer.py +15 -1
  100. claude_mpm/services/socketio/server/core.py +160 -21
  101. claude_mpm/services/version_control/git_operations.py +103 -0
  102. claude_mpm/utils/agent_filters.py +261 -0
  103. claude_mpm/utils/gitignore.py +3 -0
  104. claude_mpm/utils/migration.py +372 -0
  105. claude_mpm/utils/progress.py +5 -1
  106. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/METADATA +69 -84
  107. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/RECORD +112 -153
  108. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/entry_points.txt +0 -2
  109. claude_mpm/dashboard/analysis_runner.py +0 -455
  110. claude_mpm/dashboard/index.html +0 -13
  111. claude_mpm/dashboard/open_dashboard.py +0 -66
  112. claude_mpm/dashboard/static/css/activity.css +0 -1958
  113. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  114. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  115. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  116. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  117. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  118. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  119. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  120. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  121. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  122. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  123. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  124. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  125. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  126. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  127. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  128. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  129. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  130. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  131. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  132. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  133. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  134. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  135. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  136. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  137. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  138. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  139. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  140. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  141. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  142. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  143. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  144. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  145. claude_mpm/dashboard/templates/code_simple.html +0 -153
  146. claude_mpm/dashboard/templates/index.html +0 -606
  147. claude_mpm/dashboard/test_dashboard.html +0 -372
  148. claude_mpm/scripts/mcp_server.py +0 -75
  149. claude_mpm/scripts/mcp_wrapper.py +0 -39
  150. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  151. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  152. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  153. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  154. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  155. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  156. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  157. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  158. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  159. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  160. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -971
  161. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  162. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  163. claude_mpm/services/mcp_gateway/main.py +0 -589
  164. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  165. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  166. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  167. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  168. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  169. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  170. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  171. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  172. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  173. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  174. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  175. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  176. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  177. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  178. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  179. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  180. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  181. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  182. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/WHEEL +0 -0
  183. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/licenses/LICENSE +0 -0
  184. {claude_mpm-5.0.2.dist-info → claude_mpm-5.4.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,413 @@
1
+ """
2
+ Document Summarization Command.
3
+
4
+ Shell-based alternative to MCP document_summarizer tool.
5
+ Provides algorithmic summarization without ML dependencies.
6
+
7
+ Design Decision: Uses simple text processing techniques:
8
+ - Brief: First paragraph extraction
9
+ - Detailed: Key sentence extraction based on position and length
10
+ - Bullet Points: Convert to markdown bullet list
11
+ - Executive: Opening + conclusion extraction
12
+
13
+ Why: Lightweight, fast, no dependencies, works offline.
14
+ """
15
+
16
+ import json
17
+ import re
18
+ from enum import Enum
19
+ from pathlib import Path
20
+ from typing import Optional
21
+
22
+
23
+ class SummaryStyle(str, Enum):
24
+ """Summary output styles."""
25
+
26
+ BRIEF = "brief"
27
+ DETAILED = "detailed"
28
+ BULLET_POINTS = "bullet_points"
29
+ EXECUTIVE = "executive"
30
+
31
+
32
+ class OutputFormat(str, Enum):
33
+ """Output format types."""
34
+
35
+ TEXT = "text"
36
+ JSON = "json"
37
+ MARKDOWN = "markdown"
38
+
39
+
40
+ class DocumentSummarizer:
41
+ """
42
+ Algorithmic document summarizer.
43
+
44
+ Design Decision: Use simple heuristics instead of ML:
45
+ - Position-based extraction (opening, closing paragraphs)
46
+ - Length-based filtering (key sentences)
47
+ - Structure detection (headings, lists)
48
+
49
+ Trade-offs:
50
+ - Performance: O(n) single pass vs. complex NLP models
51
+ - Accuracy: ~70% vs. ~90% for ML models
52
+ - Simplicity: Zero dependencies vs. heavy ML packages
53
+ """
54
+
55
+ def __init__(self, max_words: int = 150):
56
+ """Initialize summarizer with word limit."""
57
+ self.max_words = max_words
58
+
59
+ def summarize(
60
+ self, content: str, style: SummaryStyle, lines_limit: Optional[int] = None
61
+ ) -> str:
62
+ """
63
+ Summarize document content.
64
+
65
+ Args:
66
+ content: Document text to summarize
67
+ style: Summary style (brief, detailed, bullet_points, executive)
68
+ lines_limit: Optional line limit (reads first N lines only)
69
+
70
+ Returns:
71
+ Summary text
72
+
73
+ Complexity: O(n) where n is content length
74
+ """
75
+ # Apply line limit if specified
76
+ if lines_limit:
77
+ content = self._limit_lines(content, lines_limit)
78
+
79
+ # Route to style-specific summarizer
80
+ summarizers = {
81
+ SummaryStyle.BRIEF: self._summarize_brief,
82
+ SummaryStyle.DETAILED: self._summarize_detailed,
83
+ SummaryStyle.BULLET_POINTS: self._summarize_bullet_points,
84
+ SummaryStyle.EXECUTIVE: self._summarize_executive,
85
+ }
86
+
87
+ summary = summarizers[style](content)
88
+ return self._truncate_to_word_limit(summary)
89
+
90
+ def _limit_lines(self, content: str, limit: int) -> str:
91
+ """Limit content to first N lines."""
92
+ lines = content.split("\n")
93
+ return "\n".join(lines[:limit])
94
+
95
+ def _truncate_to_word_limit(self, text: str) -> str:
96
+ """Truncate text to max_words limit."""
97
+ words = text.split()
98
+ if len(words) <= self.max_words:
99
+ return text
100
+
101
+ # Truncate and add ellipsis
102
+ truncated = " ".join(words[: self.max_words])
103
+ return f"{truncated}..."
104
+
105
+ def _summarize_brief(self, content: str) -> str:
106
+ """
107
+ Brief summary: Extract first paragraph.
108
+
109
+ Heuristic: First non-empty paragraph usually introduces document.
110
+ """
111
+ paragraphs = self._extract_paragraphs(content)
112
+ if not paragraphs:
113
+ return content.strip()
114
+
115
+ return paragraphs[0]
116
+
117
+ def _summarize_detailed(self, content: str) -> str:
118
+ """
119
+ Detailed summary: Extract key sentences.
120
+
121
+ Heuristics:
122
+ - First paragraph (introduction)
123
+ - Sentences with important markers (however, therefore, important)
124
+ - Last paragraph (conclusion)
125
+ """
126
+ paragraphs = self._extract_paragraphs(content)
127
+ if not paragraphs:
128
+ return content.strip()
129
+
130
+ key_sentences = []
131
+
132
+ # Add first paragraph
133
+ if paragraphs:
134
+ key_sentences.append(paragraphs[0])
135
+
136
+ # Add sentences with key markers from middle paragraphs
137
+ if len(paragraphs) > 2:
138
+ key_markers = [
139
+ "however",
140
+ "therefore",
141
+ "important",
142
+ "note",
143
+ "critical",
144
+ "key",
145
+ "must",
146
+ "should",
147
+ "recommended",
148
+ ]
149
+
150
+ for para in paragraphs[1:-1]:
151
+ sentences = self._split_sentences(para)
152
+ for sentence in sentences:
153
+ if any(marker in sentence.lower() for marker in key_markers):
154
+ key_sentences.append(sentence)
155
+ break # One sentence per paragraph max
156
+
157
+ # Add last paragraph
158
+ if len(paragraphs) > 1:
159
+ key_sentences.append(paragraphs[-1])
160
+
161
+ return " ".join(key_sentences)
162
+
163
+ def _summarize_bullet_points(self, content: str) -> str:
164
+ """
165
+ Bullet point summary: Convert paragraphs to markdown list.
166
+
167
+ Heuristic: Each paragraph becomes a bullet point.
168
+ """
169
+ paragraphs = self._extract_paragraphs(content)
170
+ if not paragraphs:
171
+ return content.strip()
172
+
173
+ # Take key paragraphs (first, middle with markers, last)
174
+ key_paragraphs = []
175
+
176
+ # Always include first
177
+ if paragraphs:
178
+ key_paragraphs.append(paragraphs[0])
179
+
180
+ # Include middle paragraphs with key content
181
+ if len(paragraphs) > 2:
182
+ key_markers = ["however", "therefore", "important", "note", "critical"]
183
+ for para in paragraphs[1:-1]:
184
+ if any(marker in para.lower() for marker in key_markers):
185
+ # Take first sentence only for bullet point
186
+ first_sentence = self._split_sentences(para)[0]
187
+ key_paragraphs.append(first_sentence)
188
+
189
+ # Include last if different from first
190
+ if len(paragraphs) > 1:
191
+ key_paragraphs.append(paragraphs[-1])
192
+
193
+ # Format as markdown bullets
194
+ bullets = [f"- {para}" for para in key_paragraphs]
195
+ return "\n".join(bullets)
196
+
197
+ def _summarize_executive(self, content: str) -> str:
198
+ """
199
+ Executive summary: Opening + conclusion.
200
+
201
+ Heuristic: First and last paragraphs capture overview and conclusion.
202
+ """
203
+ paragraphs = self._extract_paragraphs(content)
204
+ if not paragraphs:
205
+ return content.strip()
206
+
207
+ if len(paragraphs) == 1:
208
+ return paragraphs[0]
209
+
210
+ # Opening paragraph + conclusion paragraph
211
+ return f"{paragraphs[0]}\n\n{paragraphs[-1]}"
212
+
213
+ def _extract_paragraphs(self, content: str) -> list[str]:
214
+ """
215
+ Extract paragraphs from content.
216
+
217
+ Filters out:
218
+ - Empty lines
219
+ - Short lines (< 40 chars, likely headers/formatting artifacts)
220
+ - Code blocks (lines with multiple indentation)
221
+ - Lines that look like code (contain def, class, =, {, etc.)
222
+ """
223
+ # Split on double newlines for paragraph boundaries
224
+ raw_paragraphs = re.split(r"\n\s*\n", content)
225
+
226
+ paragraphs = []
227
+ for para in raw_paragraphs:
228
+ # Clean and normalize whitespace
229
+ para = " ".join(para.split())
230
+
231
+ # Skip empty or very short paragraphs (likely headers)
232
+ if len(para) < 40:
233
+ continue
234
+
235
+ # Skip code blocks (heuristic: contains code-like patterns)
236
+ code_indicators = ["def ", "class ", " = ", "{", "}", "return ", "import "]
237
+ if any(indicator in para for indicator in code_indicators):
238
+ continue
239
+
240
+ paragraphs.append(para)
241
+
242
+ return paragraphs
243
+
244
+ def _split_sentences(self, text: str) -> list[str]:
245
+ """
246
+ Split text into sentences.
247
+
248
+ Simple heuristic: Split on '. ' but handle common abbreviations.
249
+ """
250
+ # Handle common abbreviations to avoid false splits
251
+ text = text.replace("Dr.", "Dr<DOT>")
252
+ text = text.replace("Mr.", "Mr<DOT>")
253
+ text = text.replace("Mrs.", "Mrs<DOT>")
254
+ text = text.replace("e.g.", "e<DOT>g<DOT>")
255
+ text = text.replace("i.e.", "i<DOT>e<DOT>")
256
+
257
+ # Split on sentence boundaries
258
+ sentences = re.split(r"(?<=[.!?])\s+", text)
259
+
260
+ # Restore abbreviations
261
+ sentences = [s.replace("<DOT>", ".") for s in sentences]
262
+
263
+ return [s.strip() for s in sentences if s.strip()]
264
+
265
+
266
+ def format_output(summary: str, output_format: OutputFormat, file_path: Path) -> str:
267
+ """
268
+ Format summary output.
269
+
270
+ Args:
271
+ summary: Summary text
272
+ output_format: Output format (text, json, markdown)
273
+ file_path: Original file path for metadata
274
+
275
+ Returns:
276
+ Formatted output string
277
+ """
278
+ if output_format == OutputFormat.TEXT:
279
+ return summary
280
+
281
+ if output_format == OutputFormat.JSON:
282
+ result = {
283
+ "file": str(file_path),
284
+ "summary": summary,
285
+ "word_count": len(summary.split()),
286
+ }
287
+ return json.dumps(result, indent=2)
288
+
289
+ if output_format == OutputFormat.MARKDOWN:
290
+ return f"# Summary: {file_path.name}\n\n{summary}\n"
291
+
292
+ return summary
293
+
294
+
295
+ def summarize_command(args) -> int:
296
+ """
297
+ Execute summarize command.
298
+
299
+ Args:
300
+ args: Parsed command line arguments with:
301
+ - file_path: Path to file to summarize
302
+ - style: Summary style
303
+ - max_words: Maximum words in summary
304
+ - output: Output format
305
+ - lines: Optional line limit
306
+
307
+ Returns:
308
+ Exit code (0 for success, 1 for error)
309
+ """
310
+ file_path = Path(args.file_path)
311
+
312
+ # Validate file exists
313
+ if not file_path.exists():
314
+ print(f"Error: File not found: {file_path}")
315
+ return 1
316
+
317
+ if not file_path.is_file():
318
+ print(f"Error: Not a file: {file_path}")
319
+ return 1
320
+
321
+ try:
322
+ # Read file content
323
+ content = file_path.read_text(encoding="utf-8")
324
+
325
+ # Create summarizer
326
+ summarizer = DocumentSummarizer(max_words=args.max_words)
327
+
328
+ # Generate summary
329
+ summary = summarizer.summarize(
330
+ content, style=SummaryStyle(args.style), lines_limit=args.lines
331
+ )
332
+
333
+ # Format output
334
+ output = format_output(summary, OutputFormat(args.output), file_path)
335
+
336
+ # Print result
337
+ print(output)
338
+
339
+ return 0
340
+
341
+ except UnicodeDecodeError:
342
+ print(f"Error: Cannot read file (not valid UTF-8): {file_path}")
343
+ return 1
344
+ except Exception as e:
345
+ print(f"Error: {e}")
346
+ return 1
347
+
348
+
349
+ def add_summarize_parser(subparsers) -> None:
350
+ """
351
+ Add summarize subcommand parser.
352
+
353
+ Args:
354
+ subparsers: Subparsers object from argparse
355
+ """
356
+ parser = subparsers.add_parser(
357
+ "summarize",
358
+ help="Summarize document content (shell-based alternative to MCP document_summarizer)",
359
+ description="""
360
+ Algorithmic document summarization without ML dependencies.
361
+
362
+ Styles:
363
+ brief - First paragraph only (quick overview)
364
+ detailed - Key sentences from opening, middle, closing
365
+ bullet_points - Markdown bullet list of key points
366
+ executive - Opening + conclusion (for quick decisions)
367
+
368
+ Examples:
369
+ claude-mpm summarize README.md
370
+ claude-mpm summarize docs/guide.md --style detailed --max-words 200
371
+ claude-mpm summarize src/main.py --style bullet_points --output markdown
372
+ claude-mpm summarize large.txt --lines 100 --style brief
373
+ """,
374
+ formatter_class=lambda prog: __import__("argparse").RawDescriptionHelpFormatter(
375
+ prog, max_help_position=40
376
+ ),
377
+ )
378
+
379
+ # Required arguments
380
+ parser.add_argument("file_path", type=str, help="Path to file to summarize")
381
+
382
+ # Optional arguments
383
+ parser.add_argument(
384
+ "--style",
385
+ type=str,
386
+ choices=["brief", "detailed", "bullet_points", "executive"],
387
+ default="brief",
388
+ help="Summary style (default: brief)",
389
+ )
390
+
391
+ parser.add_argument(
392
+ "--max-words",
393
+ type=int,
394
+ default=150,
395
+ help="Maximum words in summary (default: 150)",
396
+ )
397
+
398
+ parser.add_argument(
399
+ "--output",
400
+ type=str,
401
+ choices=["text", "json", "markdown"],
402
+ default="text",
403
+ help="Output format (default: text)",
404
+ )
405
+
406
+ parser.add_argument(
407
+ "--lines",
408
+ type=int,
409
+ default=None,
410
+ help="Limit to first N lines of file (default: no limit)",
411
+ )
412
+
413
+ parser.set_defaults(command="summarize")
@@ -141,6 +141,14 @@ def execute_command(command: str, args) -> int:
141
141
  result = agent_source_command(args)
142
142
  return result if result is not None else 0
143
143
 
144
+ # Handle summarize command with lazy import
145
+ if command == "summarize":
146
+ # Lazy import to avoid loading unless needed
147
+ from .commands.summarize import summarize_command
148
+
149
+ result = summarize_command(args)
150
+ return result if result is not None else 0
151
+
144
152
  # Handle auto-configure command with lazy import
145
153
  if command == "auto-configure":
146
154
  # Lazy import to avoid loading unless needed