claude-mpm 5.1.9__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +46 -0
- claude_mpm/agents/agent_loader.py +10 -17
- claude_mpm/agents/templates/circuit-breakers.md +138 -1
- claude_mpm/cli/commands/agent_state_manager.py +8 -17
- claude_mpm/cli/commands/configure.py +1046 -149
- claude_mpm/cli/commands/configure_agent_display.py +13 -6
- claude_mpm/cli/commands/mpm_init/core.py +158 -1
- claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
- claude_mpm/cli/commands/summarize.py +413 -0
- claude_mpm/cli/executor.py +8 -0
- claude_mpm/cli/parsers/base_parser.py +5 -0
- claude_mpm/cli/startup.py +60 -53
- claude_mpm/commands/{mpm-ticket-organize.md → mpm-organize.md} +4 -5
- claude_mpm/config/agent_sources.py +27 -0
- claude_mpm/core/framework/loaders/agent_loader.py +8 -5
- claude_mpm/core/socketio_pool.py +3 -3
- claude_mpm/core/unified_agent_registry.py +5 -15
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +35 -2
- claude_mpm/hooks/claude_hooks/hook_handler.py +4 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +4 -0
- claude_mpm/scripts/launch_monitor.py +93 -13
- claude_mpm/services/agents/agent_recommendation_service.py +279 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +3 -2
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +322 -53
- claude_mpm/services/agents/git_source_manager.py +20 -0
- claude_mpm/services/agents/sources/git_source_sync_service.py +8 -1
- claude_mpm/services/agents/toolchain_detector.py +6 -5
- claude_mpm/services/analysis/__init__.py +11 -1
- claude_mpm/services/analysis/clone_detector.py +1030 -0
- claude_mpm/services/command_deployment_service.py +0 -2
- claude_mpm/services/event_bus/config.py +3 -1
- claude_mpm/services/monitor/daemon.py +9 -2
- claude_mpm/services/monitor/daemon_manager.py +39 -3
- claude_mpm/services/monitor/server.py +225 -19
- claude_mpm/services/socketio/event_normalizer.py +15 -1
- claude_mpm/services/socketio/server/core.py +160 -21
- claude_mpm/services/version_control/git_operations.py +103 -0
- claude_mpm/utils/agent_filters.py +17 -44
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.3.dist-info}/METADATA +1 -77
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.3.dist-info}/RECORD +59 -114
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.3.dist-info}/entry_points.txt +0 -2
- claude_mpm/dashboard/analysis_runner.py +0 -455
- claude_mpm/dashboard/index.html +0 -13
- claude_mpm/dashboard/open_dashboard.py +0 -66
- claude_mpm/dashboard/static/css/activity.css +0 -1958
- claude_mpm/dashboard/static/css/connection-status.css +0 -370
- claude_mpm/dashboard/static/css/dashboard.css +0 -4701
- claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
- claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
- claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
- claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
- claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
- claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
- claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
- claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
- claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
- claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
- claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
- claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
- claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
- claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
- claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
- claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
- claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
- claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
- claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
- claude_mpm/dashboard/static/js/connection-manager.js +0 -536
- claude_mpm/dashboard/static/js/dashboard.js +0 -1914
- claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
- claude_mpm/dashboard/static/js/socket-client.js +0 -1474
- claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
- claude_mpm/dashboard/static/socket.io.min.js +0 -7
- claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
- claude_mpm/dashboard/templates/code_simple.html +0 -153
- claude_mpm/dashboard/templates/index.html +0 -606
- claude_mpm/dashboard/test_dashboard.html +0 -372
- claude_mpm/scripts/mcp_server.py +0 -75
- claude_mpm/scripts/mcp_wrapper.py +0 -39
- claude_mpm/services/mcp_gateway/__init__.py +0 -159
- claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
- claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
- claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
- claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
- claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
- claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
- claude_mpm/services/mcp_gateway/core/base.py +0 -312
- claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
- claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
- claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
- claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
- claude_mpm/services/mcp_gateway/main.py +0 -589
- claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
- claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
- claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
- claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
- claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
- claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
- claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
- claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
- claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
- claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
- claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.3.dist-info}/WHEEL +0 -0
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.3.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.3.dist-info}/top_level.txt +0 -0
|
@@ -10,7 +10,9 @@ from pathlib import Path
|
|
|
10
10
|
from typing import Any, Dict, Optional
|
|
11
11
|
|
|
12
12
|
__all__ = [
|
|
13
|
+
"build_enhanced_update_prompt",
|
|
13
14
|
"build_initialization_prompt",
|
|
15
|
+
"build_prompt_engineer_optimization_prompt",
|
|
14
16
|
"build_research_context_prompt",
|
|
15
17
|
"build_update_prompt",
|
|
16
18
|
]
|
|
@@ -305,6 +307,208 @@ preserving valuable project-specific information while refreshing standard secti
|
|
|
305
307
|
return prompt
|
|
306
308
|
|
|
307
309
|
|
|
310
|
+
def build_enhanced_update_prompt(
|
|
311
|
+
project_path: Path,
|
|
312
|
+
doc_analysis: Dict[str, Any],
|
|
313
|
+
git_insights: Dict[str, Any],
|
|
314
|
+
log_insights: Dict[str, Any],
|
|
315
|
+
memory_insights: Dict[str, Any],
|
|
316
|
+
project_type: Optional[str] = None,
|
|
317
|
+
framework: Optional[str] = None,
|
|
318
|
+
ast_analysis: bool = True,
|
|
319
|
+
preserve_custom: bool = True,
|
|
320
|
+
) -> str:
|
|
321
|
+
"""
|
|
322
|
+
Build enhanced update prompt with extracted knowledge from multiple sources.
|
|
323
|
+
|
|
324
|
+
This is the AUTO-DETECTED update mode that enriches CLAUDE.md with:
|
|
325
|
+
- Git history insights (architectural decisions, tech changes, workflows)
|
|
326
|
+
- Session log learnings (completed work, patterns)
|
|
327
|
+
- Memory file knowledge (accumulated project wisdom)
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
project_path: Path to the project directory
|
|
331
|
+
doc_analysis: Analysis results from DocumentationManager
|
|
332
|
+
git_insights: Extracted git history insights
|
|
333
|
+
log_insights: Extracted session log insights
|
|
334
|
+
memory_insights: Extracted memory file insights
|
|
335
|
+
project_type: Type of project (web, api, cli, library, etc.)
|
|
336
|
+
framework: Specific framework if applicable
|
|
337
|
+
ast_analysis: Enable AST analysis for enhanced documentation
|
|
338
|
+
preserve_custom: Preserve custom sections when updating
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
Formatted prompt string for enhanced update mode
|
|
342
|
+
"""
|
|
343
|
+
prompt = f"""Please delegate this task to the Agentic Coder Optimizer agent:
|
|
344
|
+
|
|
345
|
+
ENHANCED UPDATE of existing CLAUDE.md documentation with extracted project knowledge.
|
|
346
|
+
|
|
347
|
+
Project Path: {project_path}
|
|
348
|
+
Update Mode: Knowledge-enriched smart merge
|
|
349
|
+
"""
|
|
350
|
+
if project_type:
|
|
351
|
+
prompt += f"Project Type: {project_type}\n"
|
|
352
|
+
if framework:
|
|
353
|
+
prompt += f"Framework: {framework}\n"
|
|
354
|
+
|
|
355
|
+
prompt += f"""
|
|
356
|
+
Existing Documentation Analysis:
|
|
357
|
+
- Current CLAUDE.md: {doc_analysis.get("size", 0):,} characters, {doc_analysis.get("lines", 0)} lines
|
|
358
|
+
- Has Priority Index: {"Yes" if doc_analysis.get("has_priority_index") else "No"}
|
|
359
|
+
- Custom Sections: {len(doc_analysis.get("custom_sections", []))} found
|
|
360
|
+
"""
|
|
361
|
+
if preserve_custom and doc_analysis.get("custom_sections"):
|
|
362
|
+
prompt += f"- Preserve Custom Sections: {', '.join(doc_analysis['custom_sections'][:5])}\n"
|
|
363
|
+
|
|
364
|
+
# Add extracted knowledge sections
|
|
365
|
+
prompt += "\n## Extracted Project Knowledge\n\n"
|
|
366
|
+
|
|
367
|
+
# Git insights
|
|
368
|
+
if git_insights.get("available"):
|
|
369
|
+
prompt += "### From Git History (last 90 days):\n\n"
|
|
370
|
+
|
|
371
|
+
if git_insights.get("architectural_decisions"):
|
|
372
|
+
prompt += "**Architectural Patterns Detected:**\n"
|
|
373
|
+
for decision in git_insights["architectural_decisions"][:10]:
|
|
374
|
+
prompt += f"- {decision}\n"
|
|
375
|
+
prompt += "\n"
|
|
376
|
+
|
|
377
|
+
if git_insights.get("tech_stack_changes"):
|
|
378
|
+
prompt += "**Tech Stack Changes:**\n"
|
|
379
|
+
for change in git_insights["tech_stack_changes"][:10]:
|
|
380
|
+
prompt += f"- {change}\n"
|
|
381
|
+
prompt += "\n"
|
|
382
|
+
|
|
383
|
+
if git_insights.get("workflow_patterns"):
|
|
384
|
+
prompt += "**Common Workflows:**\n"
|
|
385
|
+
for workflow in git_insights["workflow_patterns"][:8]:
|
|
386
|
+
prompt += f"- {workflow}\n"
|
|
387
|
+
prompt += "\n"
|
|
388
|
+
|
|
389
|
+
if git_insights.get("hot_files"):
|
|
390
|
+
prompt += "**Hot Files (frequently modified):**\n"
|
|
391
|
+
for file_info in git_insights["hot_files"][:10]:
|
|
392
|
+
prompt += (
|
|
393
|
+
f"- {file_info['path']} ({file_info['modifications']} changes)\n"
|
|
394
|
+
)
|
|
395
|
+
prompt += "\n"
|
|
396
|
+
|
|
397
|
+
# Session log insights
|
|
398
|
+
if log_insights.get("available") and log_insights.get("learnings"):
|
|
399
|
+
prompt += "### From Session Logs:\n\n"
|
|
400
|
+
prompt += "**Recent Learnings from PM Summaries:**\n"
|
|
401
|
+
for learning in log_insights["learnings"][:10]:
|
|
402
|
+
source = learning.get("source", "unknown")
|
|
403
|
+
content = learning.get("content", "")
|
|
404
|
+
# Truncate long content
|
|
405
|
+
if len(content) > 200:
|
|
406
|
+
content = content[:200] + "..."
|
|
407
|
+
prompt += f"- [{source}] {content}\n"
|
|
408
|
+
prompt += "\n"
|
|
409
|
+
|
|
410
|
+
if log_insights.get("common_patterns"):
|
|
411
|
+
prompt += "**Common Task Patterns:**\n"
|
|
412
|
+
prompt += f"- {', '.join(log_insights['common_patterns'][:10])}\n\n"
|
|
413
|
+
|
|
414
|
+
# Memory insights
|
|
415
|
+
if memory_insights.get("available"):
|
|
416
|
+
has_content = False
|
|
417
|
+
|
|
418
|
+
if memory_insights.get("architectural_knowledge"):
|
|
419
|
+
has_content = True
|
|
420
|
+
prompt += "### From Agent Memories:\n\n"
|
|
421
|
+
prompt += "**Architectural Knowledge:**\n"
|
|
422
|
+
for item in memory_insights["architectural_knowledge"][:8]:
|
|
423
|
+
prompt += f"- {item}\n"
|
|
424
|
+
prompt += "\n"
|
|
425
|
+
|
|
426
|
+
if memory_insights.get("implementation_guidelines"):
|
|
427
|
+
if not has_content:
|
|
428
|
+
prompt += "### From Agent Memories:\n\n"
|
|
429
|
+
has_content = True
|
|
430
|
+
prompt += "**Implementation Guidelines:**\n"
|
|
431
|
+
for item in memory_insights["implementation_guidelines"][:8]:
|
|
432
|
+
prompt += f"- {item}\n"
|
|
433
|
+
prompt += "\n"
|
|
434
|
+
|
|
435
|
+
if memory_insights.get("common_mistakes"):
|
|
436
|
+
if not has_content:
|
|
437
|
+
prompt += "### From Agent Memories:\n\n"
|
|
438
|
+
has_content = True
|
|
439
|
+
prompt += "**Common Mistakes to Avoid:**\n"
|
|
440
|
+
for item in memory_insights["common_mistakes"][:8]:
|
|
441
|
+
prompt += f"- {item}\n"
|
|
442
|
+
prompt += "\n"
|
|
443
|
+
|
|
444
|
+
if memory_insights.get("technical_context"):
|
|
445
|
+
if not has_content:
|
|
446
|
+
prompt += "### From Agent Memories:\n\n"
|
|
447
|
+
prompt += "**Current Technical Context:**\n"
|
|
448
|
+
for item in memory_insights["technical_context"][:8]:
|
|
449
|
+
prompt += f"- {item}\n"
|
|
450
|
+
prompt += "\n"
|
|
451
|
+
|
|
452
|
+
# Add update instructions
|
|
453
|
+
prompt += """
|
|
454
|
+
## UPDATE Tasks with Knowledge Integration:
|
|
455
|
+
|
|
456
|
+
1. **Review Existing Content**:
|
|
457
|
+
- Analyze current CLAUDE.md structure and content
|
|
458
|
+
- Identify outdated or missing information
|
|
459
|
+
- Preserve valuable custom sections and project-specific knowledge
|
|
460
|
+
|
|
461
|
+
2. **Integrate Extracted Knowledge**:
|
|
462
|
+
- Merge architectural decisions from git history into Architecture section
|
|
463
|
+
- Add tech stack changes to Technology/Dependencies sections
|
|
464
|
+
- Update workflow patterns in Development Guidelines
|
|
465
|
+
- Incorporate session learnings into relevant sections
|
|
466
|
+
- Merge memory insights into appropriate documentation areas
|
|
467
|
+
- Highlight hot files as critical components
|
|
468
|
+
|
|
469
|
+
3. **Smart Content Merge**:
|
|
470
|
+
- Update project overview with recent developments
|
|
471
|
+
- Refresh architecture documentation with detected patterns
|
|
472
|
+
- Update development workflows with discovered common patterns
|
|
473
|
+
- Ensure single-path principle (ONE way to do each task)
|
|
474
|
+
- Remove duplicate or contradictory information
|
|
475
|
+
|
|
476
|
+
4. **Update Priority Organization**:
|
|
477
|
+
- Reorganize content with priority markers (🔴🟡🟢⚪)
|
|
478
|
+
- Ensure critical instructions are at the top
|
|
479
|
+
- Update priority index with all important items
|
|
480
|
+
- Promote frequently-modified files to IMPORTANT sections
|
|
481
|
+
|
|
482
|
+
5. **Refresh Technical Content**:
|
|
483
|
+
- Update build/test/deploy commands based on workflow patterns
|
|
484
|
+
- Verify tool configurations match tech stack changes
|
|
485
|
+
- Update dependency information with detected changes
|
|
486
|
+
- Refresh API documentation if applicable
|
|
487
|
+
"""
|
|
488
|
+
if ast_analysis:
|
|
489
|
+
prompt += """
|
|
490
|
+
6. **Update Code Documentation** (using Code Analyzer agent):
|
|
491
|
+
- Re-analyze code structure for changes
|
|
492
|
+
- Update API documentation
|
|
493
|
+
- Refresh architecture diagrams
|
|
494
|
+
- Document hot files and critical components
|
|
495
|
+
"""
|
|
496
|
+
prompt += """
|
|
497
|
+
7. **Final Optimization**:
|
|
498
|
+
- Ensure single-path principle throughout
|
|
499
|
+
- Validate all links and references
|
|
500
|
+
- Add/update timestamp in meta section
|
|
501
|
+
- Add "Last Updated" note mentioning knowledge extraction
|
|
502
|
+
- Verify AI agent readability
|
|
503
|
+
|
|
504
|
+
IMPORTANT: This is an ENHANCED UPDATE operation with extracted knowledge.
|
|
505
|
+
Intelligently merge insights from git history, session logs, and agent memories
|
|
506
|
+
into the existing CLAUDE.md while preserving valuable custom content.
|
|
507
|
+
The goal is to create a living document that reflects actual project evolution.
|
|
508
|
+
"""
|
|
509
|
+
return prompt
|
|
510
|
+
|
|
511
|
+
|
|
308
512
|
def build_research_context_prompt(git_analysis: Dict[str, Any], days: int) -> str:
|
|
309
513
|
"""
|
|
310
514
|
Build structured Research agent delegation prompt from git analysis.
|
|
@@ -440,3 +644,79 @@ Keep it concise (<1000 words) but actionable.
|
|
|
440
644
|
"""
|
|
441
645
|
|
|
442
646
|
return prompt
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
def build_prompt_engineer_optimization_prompt(
|
|
650
|
+
content: str, estimated_tokens: int
|
|
651
|
+
) -> str:
|
|
652
|
+
"""
|
|
653
|
+
Build prompt for prompt-engineer to optimize CLAUDE.md.
|
|
654
|
+
|
|
655
|
+
Args:
|
|
656
|
+
content: Current CLAUDE.md content to optimize
|
|
657
|
+
estimated_tokens: Estimated token count of current content
|
|
658
|
+
|
|
659
|
+
Returns:
|
|
660
|
+
Formatted prompt string for prompt-engineer optimization
|
|
661
|
+
"""
|
|
662
|
+
return f"""Please delegate this task to the Prompt Engineer agent:
|
|
663
|
+
|
|
664
|
+
Optimize this CLAUDE.md file for conciseness, clarity, and token efficiency while preserving all critical information.
|
|
665
|
+
|
|
666
|
+
## Current CLAUDE.md Statistics
|
|
667
|
+
- Estimated tokens: {estimated_tokens:,}
|
|
668
|
+
- Target reduction: 20-30% if possible
|
|
669
|
+
- Priority: Preserve ALL CRITICAL (🔴) and IMPORTANT (🟡) information
|
|
670
|
+
|
|
671
|
+
## Optimization Goals
|
|
672
|
+
|
|
673
|
+
1. **Remove Redundancy**:
|
|
674
|
+
- Eliminate duplicate information across sections
|
|
675
|
+
- Consolidate similar instructions
|
|
676
|
+
- Remove verbose explanations where brevity suffices
|
|
677
|
+
|
|
678
|
+
2. **Tighten Language**:
|
|
679
|
+
- Use fewer words to convey the same meaning
|
|
680
|
+
- Replace wordy phrases with concise alternatives
|
|
681
|
+
- Remove filler words and unnecessary qualifiers
|
|
682
|
+
|
|
683
|
+
3. **Improve Structure**:
|
|
684
|
+
- Ensure clear hierarchical organization
|
|
685
|
+
- Use priority markers (🔴 🟡 🟢 ⚪) effectively
|
|
686
|
+
- Group related information logically
|
|
687
|
+
- Maintain scannable headings
|
|
688
|
+
|
|
689
|
+
4. **Preserve Critical Content**:
|
|
690
|
+
- Keep ALL security and data handling rules (🔴)
|
|
691
|
+
- Maintain core business logic and constraints (🔴)
|
|
692
|
+
- Preserve architectural decisions (🟡)
|
|
693
|
+
- Keep essential workflows intact (🟡)
|
|
694
|
+
|
|
695
|
+
5. **Apply Claude Best Practices**:
|
|
696
|
+
- Use high-level guidance over prescriptive checklists
|
|
697
|
+
- Provide context for WHY, not just WHAT
|
|
698
|
+
- Ensure instructions are actionable and unambiguous
|
|
699
|
+
- Optimize for AI agent understanding
|
|
700
|
+
|
|
701
|
+
## Current CLAUDE.md Content
|
|
702
|
+
|
|
703
|
+
{content}
|
|
704
|
+
|
|
705
|
+
## Output Requirements
|
|
706
|
+
|
|
707
|
+
Return ONLY the optimized CLAUDE.md content with NO additional explanations.
|
|
708
|
+
The optimized version should:
|
|
709
|
+
- Reduce token count by 20-30% if feasible
|
|
710
|
+
- Maintain all CRITICAL and IMPORTANT instructions
|
|
711
|
+
- Improve clarity and scannability
|
|
712
|
+
- Follow the same structural template (Priority Index, sections with markers)
|
|
713
|
+
|
|
714
|
+
## Quality Criteria
|
|
715
|
+
|
|
716
|
+
✅ All 🔴 CRITICAL items preserved
|
|
717
|
+
✅ All 🟡 IMPORTANT items preserved
|
|
718
|
+
✅ No contradictory instructions
|
|
719
|
+
✅ Clear, concise language throughout
|
|
720
|
+
✅ Logical organization maintained
|
|
721
|
+
✅ Token count reduced meaningfully
|
|
722
|
+
"""
|
|
@@ -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")
|