claude-mpm 4.7.3__py3-none-any.whl → 4.7.5__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +0 -1
- claude_mpm/agents/templates/project_organizer.json +3 -3
- claude_mpm/cli/commands/mpm_init.py +494 -88
- claude_mpm/cli/commands/mpm_init_handler.py +4 -0
- claude_mpm/cli/parsers/mpm_init_parser.py +37 -7
- claude_mpm/commands/mpm-help.md +4 -1
- claude_mpm/commands/mpm-init.md +47 -3
- claude_mpm/commands/mpm-monitor.md +409 -0
- claude_mpm/commands/mpm-organize.md +23 -10
- claude_mpm/dashboard/static/built/shared/page-structure.js +0 -2
- claude_mpm/dashboard/static/index.html +529 -607
- claude_mpm/dashboard/static/production/main.html +0 -17
- claude_mpm/dashboard/templates/index.html +0 -17
- claude_mpm/services/project/enhanced_analyzer.py +31 -4
- claude_mpm/utils/display_helper.py +260 -0
- {claude_mpm-4.7.3.dist-info → claude_mpm-4.7.5.dist-info}/METADATA +1 -1
- {claude_mpm-4.7.3.dist-info → claude_mpm-4.7.5.dist-info}/RECORD +22 -21
- claude_mpm/dashboard/static/monitors-index.html +0 -218
- {claude_mpm-4.7.3.dist-info → claude_mpm-4.7.5.dist-info}/WHEEL +0 -0
- {claude_mpm-4.7.3.dist-info → claude_mpm-4.7.5.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.7.3.dist-info → claude_mpm-4.7.5.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.7.3.dist-info → claude_mpm-4.7.5.dist-info}/top_level.txt +0 -0
@@ -19,7 +19,6 @@ from rich.console import Console
|
|
19
19
|
from rich.panel import Panel
|
20
20
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
21
21
|
from rich.prompt import Prompt
|
22
|
-
from rich.table import Table
|
23
22
|
|
24
23
|
from claude_mpm.core.logging_utils import get_logger
|
25
24
|
|
@@ -28,6 +27,7 @@ from claude_mpm.services.project.archive_manager import ArchiveManager
|
|
28
27
|
from claude_mpm.services.project.documentation_manager import DocumentationManager
|
29
28
|
from claude_mpm.services.project.enhanced_analyzer import EnhancedProjectAnalyzer
|
30
29
|
from claude_mpm.services.project.project_organizer import ProjectOrganizer
|
30
|
+
from claude_mpm.utils.display_helper import DisplayHelper
|
31
31
|
|
32
32
|
logger = get_logger(__name__)
|
33
33
|
console = Console()
|
@@ -46,6 +46,7 @@ class MPMInitCommand:
|
|
46
46
|
self.organizer = ProjectOrganizer(self.project_path)
|
47
47
|
self.archive_manager = ArchiveManager(self.project_path)
|
48
48
|
self.analyzer = EnhancedProjectAnalyzer(self.project_path)
|
49
|
+
self.display = DisplayHelper(console)
|
49
50
|
|
50
51
|
def initialize_project(
|
51
52
|
self,
|
@@ -61,6 +62,10 @@ class MPMInitCommand:
|
|
61
62
|
preserve_custom: bool = True,
|
62
63
|
skip_archive: bool = False,
|
63
64
|
dry_run: bool = False,
|
65
|
+
quick_update: bool = False,
|
66
|
+
non_interactive: bool = False,
|
67
|
+
days: int = 30,
|
68
|
+
export: Optional[str] = None,
|
64
69
|
) -> Dict:
|
65
70
|
"""
|
66
71
|
Initialize project with Agentic Coder Optimizer standards.
|
@@ -78,6 +83,10 @@ class MPMInitCommand:
|
|
78
83
|
preserve_custom: Preserve custom sections when updating
|
79
84
|
skip_archive: Skip archiving existing files
|
80
85
|
dry_run: Show what would be done without making changes
|
86
|
+
quick_update: Perform lightweight update based on recent git activity
|
87
|
+
non_interactive: Non-interactive mode - display report only without prompting
|
88
|
+
days: Number of days for git history analysis (7, 14, 30, 60, or 90)
|
89
|
+
export: Export report to file (path or "auto" for default location)
|
81
90
|
|
82
91
|
Returns:
|
83
92
|
Dict containing initialization results
|
@@ -90,6 +99,13 @@ class MPMInitCommand:
|
|
90
99
|
if review_only:
|
91
100
|
return self._run_review_mode()
|
92
101
|
|
102
|
+
if quick_update:
|
103
|
+
return self._run_quick_update_mode(
|
104
|
+
days=days,
|
105
|
+
non_interactive=non_interactive,
|
106
|
+
export=export,
|
107
|
+
)
|
108
|
+
|
93
109
|
if has_existing and not force and not update_mode:
|
94
110
|
# Auto-select update mode if organize_files or dry_run is specified
|
95
111
|
if organize_files or dry_run:
|
@@ -411,34 +427,7 @@ The final CLAUDE.md should be a comprehensive, well-organized guide that any AI
|
|
411
427
|
|
412
428
|
def _display_documentation_status(self, analysis: Dict) -> None:
|
413
429
|
"""Display current documentation status."""
|
414
|
-
|
415
|
-
table.add_column("Property", style="cyan")
|
416
|
-
table.add_column("Value", style="white")
|
417
|
-
|
418
|
-
table.add_row("Size", f"{analysis.get('size', 0):,} characters")
|
419
|
-
table.add_row("Lines", str(analysis.get("lines", 0)))
|
420
|
-
table.add_row("Sections", str(len(analysis.get("sections", []))))
|
421
|
-
table.add_row(
|
422
|
-
"Has Priority Index", "✓" if analysis.get("has_priority_index") else "✗"
|
423
|
-
)
|
424
|
-
table.add_row(
|
425
|
-
"Has Priority Markers", "✓" if analysis.get("has_priority_markers") else "✗"
|
426
|
-
)
|
427
|
-
|
428
|
-
if analysis.get("last_modified"):
|
429
|
-
table.add_row("Last Modified", analysis["last_modified"])
|
430
|
-
|
431
|
-
console.print(table)
|
432
|
-
|
433
|
-
if analysis.get("outdated_patterns"):
|
434
|
-
console.print("\n[yellow]⚠️ Outdated patterns detected:[/yellow]")
|
435
|
-
for pattern in analysis["outdated_patterns"]:
|
436
|
-
console.print(f" • {pattern}")
|
437
|
-
|
438
|
-
if analysis.get("custom_sections"):
|
439
|
-
console.print("\n[blue][INFO]️ Custom sections found:[/blue]")
|
440
|
-
for section in analysis["custom_sections"][:5]:
|
441
|
-
console.print(f" • {section}")
|
430
|
+
self.display.display_documentation_status(analysis)
|
442
431
|
|
443
432
|
def _prompt_update_action(self) -> str:
|
444
433
|
"""Prompt user for update action."""
|
@@ -511,29 +500,26 @@ The final CLAUDE.md should be a comprehensive, well-organized guide that any AI
|
|
511
500
|
self, structure: Dict, docs: Dict, git: Optional[Dict], state: Dict
|
512
501
|
) -> None:
|
513
502
|
"""Display comprehensive review report."""
|
514
|
-
|
515
|
-
console.print("[bold]PROJECT REVIEW REPORT[/bold]")
|
516
|
-
console.print("=" * 60 + "\n")
|
503
|
+
self.display.display_header("PROJECT REVIEW REPORT")
|
517
504
|
|
518
505
|
# Project State
|
519
|
-
|
520
|
-
console.print(f" Phase: {state.get('phase', 'unknown')}")
|
506
|
+
state_data = {"Phase": state.get("phase", "unknown")}
|
521
507
|
if state.get("indicators"):
|
522
|
-
|
523
|
-
|
524
|
-
console.print(f" • {indicator}")
|
508
|
+
state_data["Indicators"] = state["indicators"][:5]
|
509
|
+
self.display.display_report_section("📊 Project State", state_data)
|
525
510
|
|
526
511
|
# Structure Report
|
527
|
-
|
528
|
-
|
529
|
-
|
512
|
+
structure_data = {
|
513
|
+
"Existing directories": len(structure.get("exists", [])),
|
514
|
+
"Missing directories": len(structure.get("missing", [])),
|
515
|
+
}
|
530
516
|
if structure.get("issues"):
|
531
|
-
|
532
|
-
|
533
|
-
|
517
|
+
structure_data["Issues found"] = len(structure["issues"])
|
518
|
+
structure_data["Issues"] = structure["issues"][:3]
|
519
|
+
self.display.display_report_section("📁 Project Structure", structure_data)
|
534
520
|
|
535
521
|
# Documentation Report
|
536
|
-
|
522
|
+
self.display.display_section_title("📚 Documentation Status")
|
537
523
|
if docs.get("exists"):
|
538
524
|
console.print(f" CLAUDE.md: Found ({docs.get('size', 0):,} chars)")
|
539
525
|
console.print(f" Sections: {len(docs.get('sections', []))}")
|
@@ -545,32 +531,426 @@ The final CLAUDE.md should be a comprehensive, well-organized guide that any AI
|
|
545
531
|
|
546
532
|
# Git Analysis
|
547
533
|
if git and git.get("git_available"):
|
548
|
-
|
549
|
-
|
534
|
+
git_metrics = {
|
535
|
+
"Commits": len(git.get("recent_commits", [])),
|
536
|
+
"Authors": git.get("authors", {}).get("total_authors", 0),
|
537
|
+
"Changed files": git.get("changed_files", {}).get("total_files", 0),
|
538
|
+
}
|
539
|
+
|
540
|
+
if git.get("branch_info"):
|
541
|
+
branch_info = git["branch_info"]
|
542
|
+
git_metrics["Current branch"] = branch_info.get(
|
543
|
+
"current_branch", "unknown"
|
544
|
+
)
|
545
|
+
|
546
|
+
self.display.display_metrics_section(
|
547
|
+
"📈 Recent Activity (30 days)", git_metrics
|
548
|
+
)
|
549
|
+
|
550
|
+
if git.get("branch_info", {}).get("has_uncommitted_changes"):
|
551
|
+
self.display.display_metric_row(
|
552
|
+
"⚠️ Uncommitted changes",
|
553
|
+
f"{git['branch_info'].get('uncommitted_files', 0)} files",
|
554
|
+
warning=True,
|
555
|
+
)
|
556
|
+
|
557
|
+
# Recommendations
|
558
|
+
if state.get("recommendations"):
|
559
|
+
self.display.display_recommendations(state["recommendations"])
|
560
|
+
|
561
|
+
self.display.display_separator()
|
562
|
+
|
563
|
+
def _run_quick_update_mode(
|
564
|
+
self,
|
565
|
+
days: int = 30,
|
566
|
+
non_interactive: bool = False,
|
567
|
+
export: Optional[str] = None,
|
568
|
+
) -> Dict:
|
569
|
+
"""Run quick update mode - lightweight update based on recent git activity."""
|
570
|
+
console.print("\n[bold cyan]⚡ Quick Update Mode[/bold cyan]\n")
|
571
|
+
console.print(
|
572
|
+
f"[dim]Analyzing recent git activity ({days} days) for lightweight documentation update...[/dim]\n"
|
573
|
+
)
|
574
|
+
|
575
|
+
if not self.analyzer.is_git_repo:
|
550
576
|
console.print(
|
551
|
-
|
577
|
+
"[yellow]⚠️ Not a git repository. Quick update requires git.[/yellow]"
|
552
578
|
)
|
553
579
|
console.print(
|
554
|
-
|
580
|
+
"[dim]Tip: Use `/mpm-init --review` for non-git projects.[/dim]\n"
|
555
581
|
)
|
582
|
+
return {
|
583
|
+
"status": "error",
|
584
|
+
"message": "Quick update requires a git repository",
|
585
|
+
}
|
556
586
|
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
587
|
+
claude_md = self.project_path / "CLAUDE.md"
|
588
|
+
if not claude_md.exists():
|
589
|
+
console.print(
|
590
|
+
"[yellow]⚠️ CLAUDE.md not found. Quick update requires existing documentation.[/yellow]"
|
591
|
+
)
|
592
|
+
console.print(
|
593
|
+
"[dim]Tip: Use `/mpm-init` to create initial documentation.[/dim]\n"
|
594
|
+
)
|
595
|
+
return {
|
596
|
+
"status": "error",
|
597
|
+
"message": "Quick update requires existing CLAUDE.md",
|
598
|
+
}
|
599
|
+
|
600
|
+
with Progress(
|
601
|
+
SpinnerColumn(),
|
602
|
+
TextColumn("[progress.description]{task.description}"),
|
603
|
+
console=console,
|
604
|
+
) as progress:
|
605
|
+
# Analyze git history
|
606
|
+
task = progress.add_task(
|
607
|
+
f"[cyan]Analyzing git history ({days} days)...", total=None
|
608
|
+
)
|
609
|
+
git_analysis = self.analyzer.analyze_git_history(days_back=days)
|
610
|
+
progress.update(task, description="[green]✓ Git analysis complete")
|
611
|
+
|
612
|
+
# Analyze current documentation
|
613
|
+
task = progress.add_task(
|
614
|
+
"[cyan]Checking documentation status...", total=None
|
615
|
+
)
|
616
|
+
doc_analysis = self.doc_manager.analyze_existing_content()
|
617
|
+
progress.update(task, description="[green]✓ Documentation analyzed")
|
618
|
+
|
619
|
+
# Generate activity report
|
620
|
+
activity_report = self._generate_activity_report(
|
621
|
+
git_analysis, doc_analysis, days
|
622
|
+
)
|
623
|
+
|
624
|
+
# Display the report
|
625
|
+
self._display_activity_report(activity_report)
|
626
|
+
|
627
|
+
# Export report if requested
|
628
|
+
if export:
|
629
|
+
export_path = self._export_activity_report(activity_report, export)
|
630
|
+
console.print(f"\n[green]✅ Report exported to: {export_path}[/green]")
|
631
|
+
|
632
|
+
# Handle non-interactive mode
|
633
|
+
if non_interactive:
|
634
|
+
console.print(
|
635
|
+
"\n[cyan]ℹ️ Non-interactive mode: Report displayed, no changes made.[/cyan]"
|
636
|
+
)
|
637
|
+
return {
|
638
|
+
"status": "success",
|
639
|
+
"mode": "quick_update",
|
640
|
+
"activity_report": activity_report,
|
641
|
+
"changes_made": False,
|
642
|
+
"non_interactive": True,
|
643
|
+
}
|
644
|
+
|
645
|
+
# Offer to append activity notes to CLAUDE.md
|
646
|
+
console.print("\n[bold]Update Options:[/bold]")
|
647
|
+
console.print(" [1] Append activity summary to CLAUDE.md")
|
648
|
+
console.print(" [2] Display report only (no changes)")
|
649
|
+
console.print(" [3] Cancel")
|
650
|
+
|
651
|
+
from rich.prompt import Prompt
|
652
|
+
|
653
|
+
choice = Prompt.ask("\nSelect option", choices=["1", "2", "3"], default="2")
|
654
|
+
|
655
|
+
if choice == "1":
|
656
|
+
# Append activity notes
|
657
|
+
self._append_activity_notes(claude_md, activity_report)
|
658
|
+
console.print("\n[green]✅ Activity notes appended to CLAUDE.md[/green]")
|
659
|
+
|
660
|
+
# Archive the update
|
661
|
+
self.archive_manager.auto_archive_before_update(
|
662
|
+
claude_md, update_reason="Quick update - recent activity summary"
|
663
|
+
)
|
664
|
+
|
665
|
+
return {
|
666
|
+
"status": "success",
|
667
|
+
"mode": "quick_update",
|
668
|
+
"activity_report": activity_report,
|
669
|
+
"changes_made": True,
|
670
|
+
}
|
671
|
+
if choice == "2":
|
672
|
+
console.print("\n[cyan]Report generated - no changes made[/cyan]")
|
673
|
+
return {
|
674
|
+
"status": "success",
|
675
|
+
"mode": "quick_update",
|
676
|
+
"activity_report": activity_report,
|
677
|
+
"changes_made": False,
|
678
|
+
}
|
679
|
+
console.print("\n[yellow]Quick update cancelled[/yellow]")
|
680
|
+
return {"status": "cancelled", "message": "Quick update cancelled"}
|
681
|
+
|
682
|
+
def _generate_activity_report(
|
683
|
+
self, git_analysis: Dict, doc_analysis: Dict, days: int = 30
|
684
|
+
) -> Dict:
|
685
|
+
"""Generate activity report from git analysis and documentation status."""
|
686
|
+
from datetime import datetime, timezone
|
687
|
+
|
688
|
+
report = {
|
689
|
+
"period": f"Last {days} days",
|
690
|
+
"generated_at": datetime.now(timezone.utc).isoformat(),
|
691
|
+
"summary": {},
|
692
|
+
"recommendations": [],
|
693
|
+
}
|
694
|
+
|
695
|
+
# Git activity summary
|
696
|
+
if git_analysis.get("git_available"):
|
697
|
+
recent_commits = git_analysis.get("recent_commits", [])
|
698
|
+
changed_files = git_analysis.get("changed_files", {})
|
699
|
+
authors = git_analysis.get("authors", {})
|
700
|
+
branch_info = git_analysis.get("branch_info", {})
|
701
|
+
|
702
|
+
report["summary"] = {
|
703
|
+
"total_commits": len(recent_commits),
|
704
|
+
"total_authors": authors.get("total_authors", 0),
|
705
|
+
"files_changed": changed_files.get("total_files", 0),
|
706
|
+
"current_branch": branch_info.get("current_branch", "unknown"),
|
707
|
+
"has_uncommitted": branch_info.get("has_uncommitted_changes", False),
|
708
|
+
"uncommitted_count": branch_info.get("uncommitted_files", 0),
|
709
|
+
}
|
710
|
+
|
711
|
+
# Recent commits (last 10)
|
712
|
+
report["recent_commits"] = recent_commits[:10]
|
713
|
+
|
714
|
+
# Most changed files
|
715
|
+
most_changed = changed_files.get("most_changed", {})
|
716
|
+
report["hot_files"] = list(most_changed.items())[:10]
|
717
|
+
|
718
|
+
# Active branches
|
719
|
+
branches = branch_info.get("branches", [])
|
720
|
+
report["active_branches"] = [
|
721
|
+
b for b in branches if not b.startswith("remotes/")
|
722
|
+
][:5]
|
723
|
+
|
724
|
+
# Generate recommendations
|
725
|
+
if len(recent_commits) > 20:
|
726
|
+
report["recommendations"].append(
|
727
|
+
"High activity detected - consider updating architecture docs"
|
561
728
|
)
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
729
|
+
|
730
|
+
if changed_files.get("total_files", 0) > 50:
|
731
|
+
report["recommendations"].append(
|
732
|
+
"Many files changed - review CLAUDE.md for accuracy"
|
733
|
+
)
|
734
|
+
|
735
|
+
if branch_info.get("has_uncommitted_changes"):
|
736
|
+
report["recommendations"].append(
|
737
|
+
"Uncommitted changes detected - commit before updating docs"
|
738
|
+
)
|
739
|
+
|
740
|
+
# Check for documentation changes
|
741
|
+
doc_changes = git_analysis.get("documentation_changes", {})
|
742
|
+
if not doc_changes.get("has_recent_doc_changes"):
|
743
|
+
report["recommendations"].append(
|
744
|
+
"No recent doc updates - CLAUDE.md may be outdated"
|
745
|
+
)
|
746
|
+
|
747
|
+
# Documentation freshness
|
748
|
+
if doc_analysis.get("exists"):
|
749
|
+
report["doc_status"] = {
|
750
|
+
"size": doc_analysis.get("size", 0),
|
751
|
+
"lines": doc_analysis.get("lines", 0),
|
752
|
+
"has_priority_index": doc_analysis.get("has_priority_index", False),
|
753
|
+
"has_priority_markers": doc_analysis.get("has_priority_markers", False),
|
754
|
+
"last_modified": doc_analysis.get("last_modified", "unknown"),
|
755
|
+
}
|
756
|
+
|
757
|
+
if not doc_analysis.get("has_priority_markers"):
|
758
|
+
report["recommendations"].append(
|
759
|
+
"Add priority markers (🔴🟡🟢⚪) to CLAUDE.md"
|
760
|
+
)
|
761
|
+
|
762
|
+
return report
|
763
|
+
|
764
|
+
def _export_activity_report(self, report: Dict, export_path: str) -> Path:
|
765
|
+
"""Export activity report to a markdown file."""
|
766
|
+
from datetime import datetime, timezone
|
767
|
+
from pathlib import Path
|
768
|
+
|
769
|
+
# Determine export path
|
770
|
+
if export_path == "auto":
|
771
|
+
# Generate default path with timestamp
|
772
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
|
773
|
+
reports_dir = self.project_path / "docs" / "reports"
|
774
|
+
reports_dir.mkdir(parents=True, exist_ok=True)
|
775
|
+
file_path = reports_dir / f"activity-report-{timestamp}.md"
|
776
|
+
else:
|
777
|
+
# Use provided path
|
778
|
+
file_path = Path(export_path)
|
779
|
+
if not file_path.is_absolute():
|
780
|
+
file_path = self.project_path / file_path
|
781
|
+
# Create parent directory if needed
|
782
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
783
|
+
|
784
|
+
# Generate markdown content
|
785
|
+
summary = report.get("summary", {})
|
786
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
|
787
|
+
|
788
|
+
content = f"""# Activity Report
|
789
|
+
|
790
|
+
**Generated**: {timestamp}
|
791
|
+
**Analysis Period**: {report.get('period', 'Last 30 days')}
|
792
|
+
|
793
|
+
## Summary
|
794
|
+
|
795
|
+
- **Total Commits**: {summary.get('total_commits', 0)}
|
796
|
+
- **Active Contributors**: {summary.get('total_authors', 0)}
|
797
|
+
- **Files Modified**: {summary.get('files_changed', 0)}
|
798
|
+
- **Current Branch**: {summary.get('current_branch', 'unknown')}
|
799
|
+
"""
|
800
|
+
|
801
|
+
if summary.get("has_uncommitted"):
|
802
|
+
content += f"- **⚠️ Uncommitted Changes**: {summary.get('uncommitted_count', 0)} files\n"
|
803
|
+
|
804
|
+
# Recent commits
|
805
|
+
recent_commits = report.get("recent_commits", [])
|
806
|
+
if recent_commits:
|
807
|
+
content += "\n## Recent Commits\n\n"
|
808
|
+
for commit in recent_commits[:10]:
|
809
|
+
content += (
|
810
|
+
f"- `{commit['hash']}` {commit['message']} - {commit['author']}\n"
|
811
|
+
)
|
812
|
+
|
813
|
+
# Hot files
|
814
|
+
hot_files = report.get("hot_files", [])
|
815
|
+
if hot_files:
|
816
|
+
content += "\n## Most Changed Files\n\n"
|
817
|
+
for hot_file_path, changes in hot_files[:10]:
|
818
|
+
content += f"- `{hot_file_path}`: {changes} changes\n"
|
819
|
+
|
820
|
+
# Active branches
|
821
|
+
branches = report.get("active_branches", [])
|
822
|
+
if branches:
|
823
|
+
content += "\n## Active Branches\n\n"
|
824
|
+
for branch in branches:
|
825
|
+
marker = "→" if branch == summary.get("current_branch") else " "
|
826
|
+
content += f"{marker} {branch}\n"
|
827
|
+
|
828
|
+
# Documentation status
|
829
|
+
doc_status = report.get("doc_status", {})
|
830
|
+
if doc_status:
|
831
|
+
content += "\n## CLAUDE.md Status\n\n"
|
832
|
+
content += f"- **Size**: {doc_status.get('size', 0):,} characters\n"
|
833
|
+
content += f"- **Lines**: {doc_status.get('lines', 0)}\n"
|
834
|
+
content += f"- **Priority Markers**: {'✓' if doc_status.get('has_priority_markers') else '✗'}\n"
|
835
|
+
content += (
|
836
|
+
f"- **Last Modified**: {doc_status.get('last_modified', 'unknown')}\n"
|
837
|
+
)
|
566
838
|
|
567
839
|
# Recommendations
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
840
|
+
recommendations = report.get("recommendations", [])
|
841
|
+
if recommendations:
|
842
|
+
content += "\n## Recommendations\n\n"
|
843
|
+
for rec in recommendations:
|
844
|
+
content += f"- {rec}\n"
|
845
|
+
|
846
|
+
content += (
|
847
|
+
"\n---\n\n*Generated by Claude MPM `/mpm-init --quick-update --export`*\n"
|
848
|
+
)
|
849
|
+
|
850
|
+
# Write to file
|
851
|
+
file_path.write_text(content, encoding="utf-8")
|
852
|
+
|
853
|
+
return file_path
|
854
|
+
|
855
|
+
def _display_activity_report(self, report: Dict) -> None:
|
856
|
+
"""Display the activity report in a formatted manner."""
|
857
|
+
self.display.display_header("RECENT ACTIVITY SUMMARY")
|
858
|
+
|
859
|
+
summary = report.get("summary", {})
|
860
|
+
period = report.get("period", "Last 30 days")
|
861
|
+
|
862
|
+
# Summary statistics
|
863
|
+
self.display.display_activity_summary(summary, period)
|
864
|
+
|
865
|
+
# Recent commits
|
866
|
+
recent_commits = report.get("recent_commits", [])
|
867
|
+
if recent_commits:
|
868
|
+
self.display.display_commit_list(recent_commits)
|
869
|
+
|
870
|
+
# Hot files
|
871
|
+
hot_files = report.get("hot_files", [])
|
872
|
+
if hot_files:
|
873
|
+
self.display.display_file_change_list(hot_files)
|
874
|
+
|
875
|
+
# Active branches
|
876
|
+
branches = report.get("active_branches", [])
|
877
|
+
current_branch = summary.get("current_branch", "unknown")
|
878
|
+
if branches:
|
879
|
+
self.display.display_branch_list(branches, current_branch)
|
880
|
+
|
881
|
+
# Documentation status
|
882
|
+
doc_status = report.get("doc_status", {})
|
883
|
+
if doc_status:
|
884
|
+
doc_metrics = {
|
885
|
+
"Size": f"{doc_status.get('size', 0):,} characters",
|
886
|
+
"Lines": doc_status.get("lines", 0),
|
887
|
+
"Priority markers": (
|
888
|
+
"✓" if doc_status.get("has_priority_markers") else "✗"
|
889
|
+
),
|
890
|
+
"Last modified": doc_status.get("last_modified", "unknown"),
|
891
|
+
}
|
892
|
+
self.display.display_metrics_section("📚 CLAUDE.md Status", doc_metrics)
|
893
|
+
|
894
|
+
# Recommendations
|
895
|
+
recommendations = report.get("recommendations", [])
|
896
|
+
if recommendations:
|
897
|
+
self.display.display_recommendations(recommendations)
|
898
|
+
|
899
|
+
self.display.display_separator()
|
900
|
+
|
901
|
+
def _append_activity_notes(self, claude_md_path: Path, report: Dict) -> None:
|
902
|
+
"""Append activity notes to CLAUDE.md."""
|
903
|
+
from datetime import datetime, timezone
|
904
|
+
|
905
|
+
# Generate activity summary section
|
906
|
+
summary = report.get("summary", {})
|
907
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
|
908
|
+
|
909
|
+
activity_section = f"""
|
910
|
+
|
911
|
+
---
|
912
|
+
|
913
|
+
## 📈 Recent Activity Summary
|
914
|
+
|
915
|
+
**Last Updated**: {timestamp}
|
916
|
+
**Analysis Period**: {report.get('period', 'Last 30 days')}
|
917
|
+
|
918
|
+
### Activity Metrics
|
919
|
+
- **Commits**: {summary.get('total_commits', 0)}
|
920
|
+
- **Contributors**: {summary.get('total_authors', 0)}
|
921
|
+
- **Files Changed**: {summary.get('files_changed', 0)}
|
922
|
+
- **Current Branch**: {summary.get('current_branch', 'unknown')}
|
923
|
+
"""
|
924
|
+
|
925
|
+
if summary.get("has_uncommitted"):
|
926
|
+
activity_section += f"- **⚠️ Uncommitted Changes**: {summary.get('uncommitted_count', 0)} files\n"
|
927
|
+
|
928
|
+
# Add recent commits
|
929
|
+
recent_commits = report.get("recent_commits", [])
|
930
|
+
if recent_commits:
|
931
|
+
activity_section += "\n### Recent Commits\n"
|
932
|
+
for commit in recent_commits[:5]:
|
933
|
+
activity_section += f"- `{commit['hash']}` {commit['message'][:60]} ({commit['author']})\n"
|
934
|
+
|
935
|
+
# Add hot files
|
936
|
+
hot_files = report.get("hot_files", [])
|
937
|
+
if hot_files:
|
938
|
+
activity_section += "\n### Most Active Files\n"
|
939
|
+
for file_path, changes in hot_files[:5]:
|
940
|
+
activity_section += f"- `{file_path}`: {changes} changes\n"
|
572
941
|
|
573
|
-
|
942
|
+
# Add recommendations
|
943
|
+
recommendations = report.get("recommendations", [])
|
944
|
+
if recommendations:
|
945
|
+
activity_section += "\n### 💡 Recommendations\n"
|
946
|
+
for rec in recommendations:
|
947
|
+
activity_section += f"- {rec}\n"
|
948
|
+
|
949
|
+
activity_section += "\n---\n"
|
950
|
+
|
951
|
+
# Append to file
|
952
|
+
with open(claude_md_path, "a", encoding="utf-8") as f:
|
953
|
+
f.write(activity_section)
|
574
954
|
|
575
955
|
def _run_dry_run_mode(self, organize_files: bool, has_existing: bool) -> Dict:
|
576
956
|
"""Run dry-run mode to show what would be done without making changes."""
|
@@ -991,39 +1371,35 @@ preserving valuable project-specific information while refreshing standard secti
|
|
991
1371
|
if result["status"] == "success":
|
992
1372
|
console.print("\n[green]✅ Project Initialization Complete![/green]\n")
|
993
1373
|
|
1374
|
+
# Display files created
|
994
1375
|
if result.get("files_created"):
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
console.print()
|
1376
|
+
self.display.display_files_list(
|
1377
|
+
"Files Created:", result["files_created"]
|
1378
|
+
)
|
999
1379
|
|
1380
|
+
# Display files updated
|
1000
1381
|
if result.get("files_updated"):
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
console.print()
|
1382
|
+
self.display.display_files_list(
|
1383
|
+
"Files Updated:", result["files_updated"]
|
1384
|
+
)
|
1005
1385
|
|
1386
|
+
# Display next steps
|
1006
1387
|
if result.get("next_steps"):
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
"• [cyan].claude-mpm/[/cyan] - Configuration and memories\n"
|
1021
|
-
"• [cyan]CODE_STRUCTURE.md[/cyan] - AST-derived architecture documentation (if enabled)\n\n"
|
1022
|
-
"[dim]Run 'claude-mpm run' to start using the optimized setup[/dim]",
|
1023
|
-
title="Success",
|
1024
|
-
border_style="green",
|
1025
|
-
)
|
1388
|
+
self.display.display_next_steps(result["next_steps"])
|
1389
|
+
|
1390
|
+
# Display success panel
|
1391
|
+
success_content = (
|
1392
|
+
"[green]Your project is now optimized for Claude Code and Claude MPM![/green]\n\n"
|
1393
|
+
"Key files:\n"
|
1394
|
+
"• [cyan]CLAUDE.md[/cyan] - Main documentation for AI agents\n"
|
1395
|
+
" - Organized with priority rankings (🔴🟡🟢⚪)\n"
|
1396
|
+
" - Instructions ranked by importance for AI understanding\n"
|
1397
|
+
" - Holistic documentation review completed\n"
|
1398
|
+
"• [cyan].claude-mpm/[/cyan] - Configuration and memories\n"
|
1399
|
+
"• [cyan]CODE_STRUCTURE.md[/cyan] - AST-derived architecture documentation (if enabled)\n\n"
|
1400
|
+
"[dim]Run 'claude-mpm run' to start using the optimized setup[/dim]"
|
1026
1401
|
)
|
1402
|
+
self.display.display_success_panel("Success", success_content)
|
1027
1403
|
|
1028
1404
|
|
1029
1405
|
@click.command(name="mpm-init")
|
@@ -1082,6 +1458,28 @@ preserving valuable project-specific information while refreshing standard secti
|
|
1082
1458
|
default=True,
|
1083
1459
|
help="Enable/disable AST analysis for enhanced documentation (default: enabled)",
|
1084
1460
|
)
|
1461
|
+
@click.option(
|
1462
|
+
"--quick-update",
|
1463
|
+
is_flag=True,
|
1464
|
+
help="Perform lightweight update based on recent git activity (default: 30 days)",
|
1465
|
+
)
|
1466
|
+
@click.option(
|
1467
|
+
"--non-interactive",
|
1468
|
+
is_flag=True,
|
1469
|
+
help="Non-interactive mode - display report only without prompting (use with --quick-update)",
|
1470
|
+
)
|
1471
|
+
@click.option(
|
1472
|
+
"--days",
|
1473
|
+
type=int,
|
1474
|
+
default=30,
|
1475
|
+
help="Number of days for git history analysis in quick update mode (default: 30)",
|
1476
|
+
)
|
1477
|
+
@click.option(
|
1478
|
+
"--export",
|
1479
|
+
type=str,
|
1480
|
+
default=None,
|
1481
|
+
help="Export activity report to file (default: docs/reports/activity-report-{timestamp}.md)",
|
1482
|
+
)
|
1085
1483
|
@click.argument(
|
1086
1484
|
"project_path",
|
1087
1485
|
type=click.Path(exists=True, file_okay=False, dir_okay=True),
|
@@ -1100,6 +1498,10 @@ def mpm_init(
|
|
1100
1498
|
skip_archive,
|
1101
1499
|
verbose,
|
1102
1500
|
ast_analysis,
|
1501
|
+
quick_update,
|
1502
|
+
non_interactive,
|
1503
|
+
days,
|
1504
|
+
export,
|
1103
1505
|
project_path,
|
1104
1506
|
):
|
1105
1507
|
"""
|
@@ -1141,6 +1543,10 @@ def mpm_init(
|
|
1141
1543
|
organize_files=organize,
|
1142
1544
|
preserve_custom=preserve_custom,
|
1143
1545
|
skip_archive=skip_archive,
|
1546
|
+
quick_update=quick_update,
|
1547
|
+
non_interactive=non_interactive,
|
1548
|
+
days=days,
|
1549
|
+
export=export,
|
1144
1550
|
)
|
1145
1551
|
|
1146
1552
|
# Exit with appropriate code
|