claude-mpm 4.14.2__py3-none-any.whl → 4.14.4__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 +227 -8
- claude_mpm/cli/__init__.py +28 -4
- claude_mpm/cli/parsers/__init__.py +7 -1
- claude_mpm/cli/parsers/base_parser.py +91 -3
- claude_mpm/cli/utils.py +52 -1
- claude_mpm/services/core/path_resolver.py +22 -6
- {claude_mpm-4.14.2.dist-info → claude_mpm-4.14.4.dist-info}/METADATA +1 -1
- {claude_mpm-4.14.2.dist-info → claude_mpm-4.14.4.dist-info}/RECORD +13 -13
- {claude_mpm-4.14.2.dist-info → claude_mpm-4.14.4.dist-info}/WHEEL +0 -0
- {claude_mpm-4.14.2.dist-info → claude_mpm-4.14.4.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.14.2.dist-info → claude_mpm-4.14.4.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.14.2.dist-info → claude_mpm-4.14.4.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.14.
|
|
1
|
+
4.14.4
|
|
@@ -595,14 +595,233 @@ def validate_pm_response(response):
|
|
|
595
595
|
|
|
596
596
|
**CRITICAL MANDATE**: PM MUST verify and track all new files created by agents during sessions.
|
|
597
597
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
598
|
+
### Decision Matrix: When to Track Files
|
|
599
|
+
|
|
600
|
+
| File Type | Track? | Reason |
|
|
601
|
+
|-----------|--------|--------|
|
|
602
|
+
| New source files (`.py`, `.js`, etc.) | ✅ YES | Production code must be versioned |
|
|
603
|
+
| New config files (`.json`, `.yaml`, etc.) | ✅ YES | Configuration changes must be tracked |
|
|
604
|
+
| New documentation (`.md` in `/docs/`) | ✅ YES | Documentation is part of deliverables |
|
|
605
|
+
| New test files (`test_*.py`, `*.test.js`) | ✅ YES | Tests are critical artifacts |
|
|
606
|
+
| New scripts (`.sh`, `.py` in `/scripts/`) | ✅ YES | Automation must be versioned |
|
|
607
|
+
| Files in `/tmp/` directory | ❌ NO | Temporary by design (gitignored) |
|
|
608
|
+
| Files in `.gitignore` | ❌ NO | Intentionally excluded |
|
|
609
|
+
| Build artifacts (`dist/`, `build/`) | ❌ NO | Generated, not source |
|
|
610
|
+
| Virtual environments (`venv/`, `node_modules/`) | ❌ NO | Dependencies, not source |
|
|
611
|
+
| Cache directories (`.pytest_cache/`, `__pycache__/`) | ❌ NO | Generated cache |
|
|
612
|
+
|
|
613
|
+
### Verification Steps (PM Must Execute)
|
|
614
|
+
|
|
615
|
+
**When an agent creates any new files, PM MUST**:
|
|
616
|
+
|
|
617
|
+
1. **Check if file should be tracked** (see matrix above)
|
|
618
|
+
2. **Run git status** to identify untracked files
|
|
619
|
+
3. **Track the file** with `git add <filepath>`
|
|
620
|
+
4. **Verify tracking** with `git status` (confirm staged/tracked)
|
|
621
|
+
5. **Commit with context** using proper commit message format
|
|
622
|
+
|
|
623
|
+
### Commit Message Format
|
|
624
|
+
|
|
625
|
+
**Required format for file tracking commits**:
|
|
626
|
+
|
|
627
|
+
```bash
|
|
628
|
+
git commit -m "feat: add {description}
|
|
629
|
+
|
|
630
|
+
- Created {file_type} for {purpose}
|
|
631
|
+
- Includes {key_features}
|
|
632
|
+
- Part of {initiative}
|
|
633
|
+
|
|
634
|
+
🤖👥 Generated with [Claude MPM](https://github.com/bobmatnyc/claude-mpm)
|
|
635
|
+
|
|
636
|
+
Co-Authored-By: Claude <noreply@anthropic.com>"
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
**Example**:
|
|
640
|
+
```bash
|
|
641
|
+
# After agent creates: src/claude_mpm/agents/templates/new_agent.json
|
|
642
|
+
git add src/claude_mpm/agents/templates/new_agent.json
|
|
643
|
+
git commit -m "feat: add new_agent template
|
|
644
|
+
|
|
645
|
+
- Created template for new agent functionality
|
|
646
|
+
- Includes routing configuration and capabilities
|
|
647
|
+
- Part of agent expansion initiative
|
|
648
|
+
|
|
649
|
+
🤖👥 Generated with [Claude MPM](https://github.com/bobmatnyc/claude-mpm)
|
|
650
|
+
|
|
651
|
+
Co-Authored-By: Claude <noreply@anthropic.com>"
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### When This Applies
|
|
655
|
+
|
|
656
|
+
**Files that MUST be tracked**:
|
|
657
|
+
- ✅ New agent templates (`.json`, `.md`)
|
|
658
|
+
- ✅ New documentation files (in `/docs/`)
|
|
659
|
+
- ✅ New test files (in `/tests/`)
|
|
660
|
+
- ✅ New scripts (in `/scripts/`)
|
|
661
|
+
- ✅ New configuration files
|
|
662
|
+
- ✅ New source code (`.py`, `.js`, `.ts`, etc.)
|
|
663
|
+
|
|
664
|
+
**Files that should NOT be tracked**:
|
|
665
|
+
- ❌ Files in `/tmp/` directory
|
|
666
|
+
- ❌ Files explicitly in `.gitignore`
|
|
667
|
+
- ❌ Build artifacts
|
|
668
|
+
- ❌ Dependencies (venv, node_modules)
|
|
669
|
+
|
|
670
|
+
### Why This Matters
|
|
671
|
+
|
|
672
|
+
- **Prevents loss of work**: All deliverables are versioned
|
|
673
|
+
- **Maintains clean git history**: Proper context for all changes
|
|
674
|
+
- **Provides context**: Future developers understand the changes
|
|
675
|
+
- **Ensures completeness**: All deliverables are accounted for
|
|
676
|
+
- **Supports release management**: Clean tracking for deployments
|
|
677
|
+
|
|
678
|
+
### PM Responsibility
|
|
679
|
+
|
|
680
|
+
**This is PM's quality assurance responsibility and CANNOT be delegated.**
|
|
681
|
+
|
|
682
|
+
- PM MUST verify tracking after ANY file creation by ANY agent
|
|
683
|
+
- PM MUST check `git status` before ending sessions
|
|
684
|
+
- PM MUST commit all trackable files with proper context
|
|
685
|
+
- PM MUST ensure no deliverable files are left untracked
|
|
686
|
+
|
|
687
|
+
### Session Resume Capability
|
|
688
|
+
|
|
689
|
+
**CRITICAL**: Git history provides session continuity. PM MUST be able to resume work at any time by inspecting git history.
|
|
690
|
+
|
|
691
|
+
#### When Starting a Session
|
|
692
|
+
|
|
693
|
+
**If git is enabled in the project**, PM SHOULD:
|
|
694
|
+
|
|
695
|
+
1. **Check recent commits** to understand previous session work:
|
|
696
|
+
```bash
|
|
697
|
+
git log --oneline -10 # Last 10 commits
|
|
698
|
+
git log --since="24 hours ago" --pretty=format:"%h %s" # Recent work
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
2. **Examine commit messages** for context:
|
|
702
|
+
- What features were implemented?
|
|
703
|
+
- What files were created/modified?
|
|
704
|
+
- What was the user working on?
|
|
705
|
+
- Were there any blockers or issues?
|
|
706
|
+
|
|
707
|
+
3. **Review uncommitted changes**:
|
|
708
|
+
```bash
|
|
709
|
+
git status # Untracked and modified files
|
|
710
|
+
git diff # Staged and unstaged changes
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
4. **Use commit context for continuity**:
|
|
714
|
+
- "I see from git history that you were working on [feature]..."
|
|
715
|
+
- "The last commit shows [work completed]..."
|
|
716
|
+
- "There are uncommitted changes in [files]..."
|
|
717
|
+
|
|
718
|
+
#### Git History as Session Memory
|
|
719
|
+
|
|
720
|
+
**Why this matters**:
|
|
721
|
+
- ✅ **Session continuity**: PM understands context from previous sessions
|
|
722
|
+
- ✅ **Work tracking**: Complete history of what agents have delivered
|
|
723
|
+
- ✅ **Context preservation**: Commit messages provide the "why" and "what"
|
|
724
|
+
- ✅ **Resume capability**: PM can pick up exactly where previous session left off
|
|
725
|
+
- ✅ **Avoid duplication**: PM knows what's already been done
|
|
726
|
+
|
|
727
|
+
#### Commands for Session Context
|
|
728
|
+
|
|
729
|
+
**Essential git commands for PM**:
|
|
730
|
+
|
|
731
|
+
```bash
|
|
732
|
+
# What was done recently?
|
|
733
|
+
git log --oneline -10
|
|
734
|
+
|
|
735
|
+
# What's in progress?
|
|
736
|
+
git status
|
|
737
|
+
|
|
738
|
+
# What files were changed in last session?
|
|
739
|
+
git log -1 --stat
|
|
740
|
+
|
|
741
|
+
# Full context of last commit
|
|
742
|
+
git log -1 --pretty=full
|
|
743
|
+
|
|
744
|
+
# What's different since last commit?
|
|
745
|
+
git diff HEAD
|
|
746
|
+
|
|
747
|
+
# Recent work with author and date
|
|
748
|
+
git log --pretty=format:"%h %an %ar: %s" -10
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
#### Example Session Resume Pattern
|
|
752
|
+
|
|
753
|
+
**Good PM behavior when resuming**:
|
|
754
|
+
|
|
755
|
+
```
|
|
756
|
+
PM: "I'm reviewing git history to understand previous session context..."
|
|
757
|
+
[Runs: git log --oneline -5]
|
|
758
|
+
[Runs: git status]
|
|
759
|
+
|
|
760
|
+
PM: "I can see from git history that:
|
|
761
|
+
- Last commit (2 hours ago): 'feat: add authentication service'
|
|
762
|
+
- 3 files were created: auth_service.py, auth_middleware.py, test_auth.py
|
|
763
|
+
- All tests are passing based on commit message
|
|
764
|
+
- There are currently no uncommitted changes
|
|
765
|
+
|
|
766
|
+
Based on this context, what would you like to work on next?"
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
**Bad PM behavior** (no git context):
|
|
770
|
+
|
|
771
|
+
```
|
|
772
|
+
PM: "What would you like to work on?"
|
|
773
|
+
[No git history check, no understanding of previous session context]
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
#### Integration with Circuit Breaker #5
|
|
777
|
+
|
|
778
|
+
**Session start verification**:
|
|
779
|
+
- ✅ PM checks git history for context
|
|
780
|
+
- ✅ PM reports any uncommitted deliverable files
|
|
781
|
+
- ✅ PM offers to commit them before starting new work
|
|
782
|
+
|
|
783
|
+
**Session end verification**:
|
|
784
|
+
- ✅ PM commits all deliverable files with context
|
|
785
|
+
- ✅ Future sessions can resume by reading these commits
|
|
786
|
+
- ✅ Git history becomes project memory
|
|
787
|
+
|
|
788
|
+
### Before Ending ANY Session
|
|
789
|
+
|
|
790
|
+
**Mandatory pre-session-end checklist**:
|
|
791
|
+
|
|
792
|
+
```bash
|
|
793
|
+
# 1. Check for untracked files
|
|
794
|
+
git status
|
|
795
|
+
|
|
796
|
+
# 2. Review untracked files against decision matrix
|
|
797
|
+
# 3. Track all deliverable files (not in /tmp/ or .gitignore)
|
|
798
|
+
git add <files>
|
|
799
|
+
|
|
800
|
+
# 4. Commit with context
|
|
801
|
+
git commit -m "feat: session deliverables
|
|
802
|
+
|
|
803
|
+
- Summary of what was created
|
|
804
|
+
- Why these files were needed
|
|
805
|
+
- Part of which initiative
|
|
806
|
+
|
|
807
|
+
🤖👥 Generated with [Claude MPM](https://github.com/bobmatnyc/claude-mpm)
|
|
808
|
+
|
|
809
|
+
Co-Authored-By: Claude <noreply@anthropic.com>"
|
|
810
|
+
|
|
811
|
+
# 5. Verify all deliverables tracked
|
|
812
|
+
git status # Should show "nothing to commit, working tree clean" (except /tmp/ and .gitignore)
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
### Circuit Breaker Integration
|
|
816
|
+
|
|
817
|
+
**Circuit Breaker #5** detects violations of this protocol:
|
|
818
|
+
|
|
819
|
+
❌ **VIOLATION**: Ending session with untracked deliverable files
|
|
820
|
+
❌ **VIOLATION**: PM not running `git status` before session end
|
|
821
|
+
❌ **VIOLATION**: PM delegating file tracking to agents (PM responsibility)
|
|
822
|
+
❌ **VIOLATION**: Committing without proper context in message
|
|
823
|
+
|
|
824
|
+
**Enforcement**: PM MUST NOT end session claiming "work complete" if deliverable files are untracked.
|
|
606
825
|
|
|
607
826
|
## SUMMARY: PM AS PURE COORDINATOR
|
|
608
827
|
|
claude_mpm/cli/__init__.py
CHANGED
|
@@ -677,7 +677,8 @@ def _execute_command(command: str, args) -> int:
|
|
|
677
677
|
lazily to avoid loading unnecessary code.
|
|
678
678
|
|
|
679
679
|
DESIGN DECISION: run_guarded is imported only when needed to maintain
|
|
680
|
-
separation between stable and experimental features.
|
|
680
|
+
separation between stable and experimental features. Command suggestions
|
|
681
|
+
are provided for unknown commands to improve user experience.
|
|
681
682
|
|
|
682
683
|
Args:
|
|
683
684
|
command: The command name to execute
|
|
@@ -768,9 +769,32 @@ def _execute_command(command: str, args) -> int:
|
|
|
768
769
|
result = command_map[command](args)
|
|
769
770
|
# Commands may return None (success) or an exit code
|
|
770
771
|
return result if result is not None else 0
|
|
771
|
-
|
|
772
|
-
#
|
|
773
|
-
|
|
772
|
+
|
|
773
|
+
# Unknown command - provide suggestions
|
|
774
|
+
from rich.console import Console
|
|
775
|
+
|
|
776
|
+
from .utils import suggest_similar_commands
|
|
777
|
+
|
|
778
|
+
console = Console(stderr=True)
|
|
779
|
+
|
|
780
|
+
console.print(f"\n[red]Error:[/red] Unknown command: {command}\n", style="bold")
|
|
781
|
+
|
|
782
|
+
# Get all valid commands for suggestions
|
|
783
|
+
all_commands = [
|
|
784
|
+
*command_map.keys(),
|
|
785
|
+
"run-guarded",
|
|
786
|
+
"uninstall",
|
|
787
|
+
"verify",
|
|
788
|
+
"auto-configure",
|
|
789
|
+
"local-deploy",
|
|
790
|
+
]
|
|
791
|
+
|
|
792
|
+
suggestion = suggest_similar_commands(command, all_commands)
|
|
793
|
+
if suggestion:
|
|
794
|
+
console.print(f"[yellow]{suggestion}[/yellow]\n")
|
|
795
|
+
|
|
796
|
+
console.print("[dim]Run 'claude-mpm --help' for usage information.[/dim]\n")
|
|
797
|
+
|
|
774
798
|
return 1
|
|
775
799
|
|
|
776
800
|
|
|
@@ -18,10 +18,16 @@ DESIGN DECISION: Each parser module handles a specific command domain:
|
|
|
18
18
|
- mcp_parser.py: MCP Gateway commands
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
from .base_parser import
|
|
21
|
+
from .base_parser import (
|
|
22
|
+
SuggestingArgumentParser,
|
|
23
|
+
add_common_arguments,
|
|
24
|
+
create_parser,
|
|
25
|
+
preprocess_args,
|
|
26
|
+
)
|
|
22
27
|
from .run_parser import add_run_arguments
|
|
23
28
|
|
|
24
29
|
__all__ = [
|
|
30
|
+
"SuggestingArgumentParser",
|
|
25
31
|
"add_common_arguments",
|
|
26
32
|
"add_run_arguments",
|
|
27
33
|
"create_parser",
|
|
@@ -11,11 +11,96 @@ and reduce duplication across command parsers.
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
import argparse
|
|
14
|
+
import sys
|
|
14
15
|
from typing import List, Optional
|
|
15
16
|
|
|
16
17
|
from ...constants import CLICommands, CLIPrefix, LogLevel
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
class SuggestingArgumentParser(argparse.ArgumentParser):
|
|
21
|
+
"""
|
|
22
|
+
Custom ArgumentParser that suggests similar commands on error.
|
|
23
|
+
|
|
24
|
+
WHY: Provides better user experience by suggesting corrections for typos
|
|
25
|
+
and invalid commands instead of just showing an error message.
|
|
26
|
+
|
|
27
|
+
DESIGN DECISION: Extends ArgumentParser.error() to add suggestions before
|
|
28
|
+
exiting. This catches all parser errors including invalid subcommands and
|
|
29
|
+
invalid options.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def error(self, message: str) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Override error method to add command suggestions.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
message: Error message from argparse
|
|
38
|
+
"""
|
|
39
|
+
from ..utils import suggest_similar_commands
|
|
40
|
+
|
|
41
|
+
# Try to extract the invalid command/option from the error message
|
|
42
|
+
invalid_value = None
|
|
43
|
+
valid_choices = []
|
|
44
|
+
|
|
45
|
+
# Handle invalid subcommand errors
|
|
46
|
+
# Format: "argument COMMAND: invalid choice: 'tickts' (choose from ...)"
|
|
47
|
+
if "invalid choice:" in message:
|
|
48
|
+
try:
|
|
49
|
+
# Extract the invalid choice
|
|
50
|
+
parts = message.split("invalid choice: '")
|
|
51
|
+
if len(parts) > 1:
|
|
52
|
+
invalid_value = parts[1].split("'")[0]
|
|
53
|
+
|
|
54
|
+
# Extract valid choices
|
|
55
|
+
if "(choose from" in message:
|
|
56
|
+
choices_part = message.split("(choose from")[1]
|
|
57
|
+
# Remove trailing parenthesis and split
|
|
58
|
+
choices_str = choices_part.rstrip(")")
|
|
59
|
+
# Parse choices - they may be quoted or unquoted
|
|
60
|
+
valid_choices = [
|
|
61
|
+
c.strip().strip("'\"")
|
|
62
|
+
for c in choices_str.split(",")
|
|
63
|
+
if c.strip()
|
|
64
|
+
]
|
|
65
|
+
except (IndexError, ValueError):
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
# Handle unrecognized arguments (invalid options)
|
|
69
|
+
# Format: "unrecognized arguments: --verbos"
|
|
70
|
+
elif "unrecognized arguments:" in message:
|
|
71
|
+
try:
|
|
72
|
+
parts = message.split("unrecognized arguments:")
|
|
73
|
+
if len(parts) > 1:
|
|
74
|
+
invalid_value = parts[1].strip().split()[0]
|
|
75
|
+
|
|
76
|
+
# Get common options from parser
|
|
77
|
+
valid_choices = []
|
|
78
|
+
for action in self._actions:
|
|
79
|
+
for option in action.option_strings:
|
|
80
|
+
valid_choices.append(option)
|
|
81
|
+
except (IndexError, ValueError):
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
# Build error message with suggestions
|
|
85
|
+
from rich.console import Console
|
|
86
|
+
|
|
87
|
+
console = Console(stderr=True)
|
|
88
|
+
|
|
89
|
+
console.print(f"\n[red]Error:[/red] {message}\n", style="bold")
|
|
90
|
+
|
|
91
|
+
# Add suggestions if we found valid choices
|
|
92
|
+
if invalid_value and valid_choices:
|
|
93
|
+
suggestion = suggest_similar_commands(invalid_value, valid_choices)
|
|
94
|
+
if suggestion:
|
|
95
|
+
console.print(f"[yellow]{suggestion}[/yellow]\n")
|
|
96
|
+
|
|
97
|
+
# Show help hint
|
|
98
|
+
console.print(f"[dim]Run '{self.prog} --help' for usage information.[/dim]\n")
|
|
99
|
+
|
|
100
|
+
# Exit with error code
|
|
101
|
+
sys.exit(2)
|
|
102
|
+
|
|
103
|
+
|
|
19
104
|
def _get_enhanced_version(base_version: str) -> str:
|
|
20
105
|
"""
|
|
21
106
|
Get enhanced version string with build number if available.
|
|
@@ -111,15 +196,18 @@ def create_main_parser(
|
|
|
111
196
|
WHY: This creates the foundation parser that other modules will extend
|
|
112
197
|
with their specific subcommands and arguments.
|
|
113
198
|
|
|
199
|
+
DESIGN DECISION: Uses SuggestingArgumentParser to provide helpful suggestions
|
|
200
|
+
for typos and invalid commands, improving user experience.
|
|
201
|
+
|
|
114
202
|
Args:
|
|
115
203
|
prog_name: The program name to use
|
|
116
204
|
version: The version string to display
|
|
117
205
|
|
|
118
206
|
Returns:
|
|
119
|
-
Configured
|
|
207
|
+
Configured SuggestingArgumentParser instance ready for subparser addition
|
|
120
208
|
"""
|
|
121
|
-
# Main parser
|
|
122
|
-
parser =
|
|
209
|
+
# Main parser with suggestion support
|
|
210
|
+
parser = SuggestingArgumentParser(
|
|
123
211
|
prog=prog_name,
|
|
124
212
|
description=f"Claude Multi-Agent Project Manager v{version} - Orchestrate Claude with agent delegation and ticket tracking",
|
|
125
213
|
epilog="By default, runs an orchestrated Claude session. Use 'claude-mpm' for interactive mode or 'claude-mpm -i \"prompt\"' for non-interactive mode.\n\nTo pass arguments to Claude CLI, use -- separator: claude-mpm run -- --model sonnet --temperature 0.1",
|
claude_mpm/cli/utils.py
CHANGED
|
@@ -6,9 +6,10 @@ Centralizing these functions reduces code duplication and provides a single plac
|
|
|
6
6
|
for common CLI operations.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
+
import difflib
|
|
9
10
|
import sys
|
|
10
11
|
from pathlib import Path
|
|
11
|
-
from typing import Optional
|
|
12
|
+
from typing import List, Optional
|
|
12
13
|
|
|
13
14
|
from ..core.logger import get_logger
|
|
14
15
|
|
|
@@ -207,3 +208,53 @@ def ensure_directories() -> None:
|
|
|
207
208
|
# Continue even if initialization fails
|
|
208
209
|
# The individual commands will handle missing directories as needed
|
|
209
210
|
pass
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def suggest_similar_commands(
|
|
214
|
+
invalid_command: str,
|
|
215
|
+
valid_commands: List[str],
|
|
216
|
+
cutoff: float = 0.6,
|
|
217
|
+
max_suggestions: int = 3,
|
|
218
|
+
) -> Optional[str]:
|
|
219
|
+
"""
|
|
220
|
+
Suggest similar commands for an invalid command using fuzzy matching.
|
|
221
|
+
|
|
222
|
+
WHY: Helps users quickly identify typos and discover correct command names
|
|
223
|
+
by suggesting the most similar valid commands. Uses stdlib difflib for
|
|
224
|
+
zero-dependency fuzzy matching.
|
|
225
|
+
|
|
226
|
+
DESIGN DECISION: Using difflib.get_close_matches provides good results for
|
|
227
|
+
typos and partial matches while being simple and lightweight (no external deps).
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
invalid_command: The invalid command the user typed
|
|
231
|
+
valid_commands: List of valid commands to match against
|
|
232
|
+
cutoff: Similarity threshold (0.0-1.0), default 0.6
|
|
233
|
+
max_suggestions: Maximum number of suggestions to return, default 3
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
Formatted suggestion string or None if no good matches found
|
|
237
|
+
|
|
238
|
+
Examples:
|
|
239
|
+
>>> suggest_similar_commands("tickts", ["tickets", "run", "agents"])
|
|
240
|
+
"Did you mean 'tickets'?"
|
|
241
|
+
|
|
242
|
+
>>> suggest_similar_commands("mem", ["memory", "monitor", "mcp"])
|
|
243
|
+
"Did you mean one of these?\n memory\n monitor\n mcp"
|
|
244
|
+
|
|
245
|
+
>>> suggest_similar_commands("xyz", ["tickets", "run"])
|
|
246
|
+
None # No good matches
|
|
247
|
+
"""
|
|
248
|
+
# Use difflib to find close matches
|
|
249
|
+
matches = difflib.get_close_matches(
|
|
250
|
+
invalid_command, valid_commands, n=max_suggestions, cutoff=cutoff
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
if not matches:
|
|
254
|
+
return None
|
|
255
|
+
|
|
256
|
+
# Format suggestion message
|
|
257
|
+
if len(matches) == 1:
|
|
258
|
+
return f"Did you mean '{matches[0]}'?"
|
|
259
|
+
suggestions = "\n ".join(matches)
|
|
260
|
+
return f"Did you mean one of these?\n {suggestions}"
|
|
@@ -13,6 +13,7 @@ that was previously embedded in FrameworkLoader. It manages:
|
|
|
13
13
|
The service consolidates path management logic while maintaining backward compatibility.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
+
import os
|
|
16
17
|
import subprocess
|
|
17
18
|
from enum import Enum
|
|
18
19
|
from pathlib import Path
|
|
@@ -74,10 +75,25 @@ class PathResolver(IPathResolver):
|
|
|
74
75
|
return path_obj
|
|
75
76
|
|
|
76
77
|
if base_dir is None:
|
|
77
|
-
base_dir =
|
|
78
|
+
base_dir = self._get_working_dir()
|
|
78
79
|
|
|
79
80
|
return (base_dir / path_obj).resolve()
|
|
80
81
|
|
|
82
|
+
def _get_working_dir(self) -> Path:
|
|
83
|
+
"""Get working directory respecting CLAUDE_MPM_USER_PWD.
|
|
84
|
+
|
|
85
|
+
When Claude MPM runs from a global installation, CLAUDE_MPM_USER_PWD
|
|
86
|
+
contains the user's actual working directory. This ensures project-local
|
|
87
|
+
paths are resolved correctly.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Path: The user's working directory
|
|
91
|
+
"""
|
|
92
|
+
user_pwd = os.environ.get("CLAUDE_MPM_USER_PWD")
|
|
93
|
+
if user_pwd:
|
|
94
|
+
return Path(user_pwd)
|
|
95
|
+
return Path.cwd()
|
|
96
|
+
|
|
81
97
|
def validate_path(self, path: Path, must_exist: bool = False) -> bool:
|
|
82
98
|
"""
|
|
83
99
|
Validate a path for security and existence.
|
|
@@ -129,7 +145,7 @@ class PathResolver(IPathResolver):
|
|
|
129
145
|
Project root path or None if not found
|
|
130
146
|
"""
|
|
131
147
|
if start_path is None:
|
|
132
|
-
start_path =
|
|
148
|
+
start_path = self._get_working_dir()
|
|
133
149
|
|
|
134
150
|
start_path = start_path.resolve()
|
|
135
151
|
|
|
@@ -299,7 +315,7 @@ class PathResolver(IPathResolver):
|
|
|
299
315
|
paths = {"project": None, "user": None, "system": None}
|
|
300
316
|
|
|
301
317
|
# Project-specific instructions
|
|
302
|
-
project_path =
|
|
318
|
+
project_path = self._get_working_dir() / ".claude-mpm" / "INSTRUCTIONS.md"
|
|
303
319
|
if project_path.exists():
|
|
304
320
|
paths["project"] = project_path
|
|
305
321
|
|
|
@@ -423,11 +439,11 @@ class PathResolver(IPathResolver):
|
|
|
423
439
|
"""Check common locations for claude-mpm."""
|
|
424
440
|
candidates = [
|
|
425
441
|
# Current directory (if we're already in claude-mpm)
|
|
426
|
-
|
|
442
|
+
self._get_working_dir(),
|
|
427
443
|
# Development location
|
|
428
444
|
Path.home() / "Projects" / "claude-mpm",
|
|
429
445
|
# Current directory subdirectory
|
|
430
|
-
|
|
446
|
+
self._get_working_dir() / "claude-mpm",
|
|
431
447
|
]
|
|
432
448
|
|
|
433
449
|
for candidate in candidates:
|
|
@@ -487,7 +503,7 @@ class PathResolver(IPathResolver):
|
|
|
487
503
|
pass
|
|
488
504
|
|
|
489
505
|
# Check if we're in development
|
|
490
|
-
if (
|
|
506
|
+
if (self._get_working_dir() / "pyproject.toml").exists():
|
|
491
507
|
return DeploymentContext.DEVELOPMENT
|
|
492
508
|
|
|
493
509
|
return DeploymentContext.UNKNOWN
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=9JfxhnDtr-8l3kCP2U5TVXSErptHoga8m7XA8zqgGOc,4
|
|
2
|
-
claude_mpm/VERSION,sha256=
|
|
2
|
+
claude_mpm/VERSION,sha256=9um9_LQSG6lSdoZhS8H1DhcAq_sJyngKhAeRay0lm3w,7
|
|
3
3
|
claude_mpm/__init__.py,sha256=UCw6j9e_tZQ3kJtTqmdfNv7MHyw9nD1jkj80WurwM2g,2064
|
|
4
4
|
claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
|
|
5
5
|
claude_mpm/constants.py,sha256=sLjJF6Kw7H4V9WWeaEYltM-77TgXqzEMX5vx4ukM5-0,5977
|
|
@@ -16,7 +16,7 @@ claude_mpm/agents/BASE_RESEARCH.md,sha256=2DZhDd5XxWWtsyNTBIwvtNWBu1JpFy5R5SAZDH
|
|
|
16
16
|
claude_mpm/agents/INSTRUCTIONS_OLD_DEPRECATED.md,sha256=zQZhrhVq9NLCtSjVX-aC0xcgueemSuPhKyv0SjEOyIQ,25852
|
|
17
17
|
claude_mpm/agents/MEMORY.md,sha256=KbRwY_RYdOvTvFC2DD-ATfwjHkQWJ5PIjlGws_7RmjI,3307
|
|
18
18
|
claude_mpm/agents/OUTPUT_STYLE.md,sha256=IYbo4jmICihrfnChbdrRpwVk4VobCcNyYqZqd53VXMk,533
|
|
19
|
-
claude_mpm/agents/PM_INSTRUCTIONS.md,sha256=
|
|
19
|
+
claude_mpm/agents/PM_INSTRUCTIONS.md,sha256=_E-J-4USZcADyJZ2280gX-AO8mLgPmiO2Cyf-Klt-7k,37024
|
|
20
20
|
claude_mpm/agents/WORKFLOW.md,sha256=vJ9iXCVqAaeM_yVlXxbcP3bsL210-1BI3ZAanvWv4hI,9085
|
|
21
21
|
claude_mpm/agents/__init__.py,sha256=jRFxvV_DIZ-NdENa-703Xu3YpwvlQj6yv-mQ6FgmldM,3220
|
|
22
22
|
claude_mpm/agents/agent-template.yaml,sha256=mRlz5Yd0SmknTeoJWgFkZXzEF5T7OmGBJGs2-KPT93k,1969
|
|
@@ -76,12 +76,12 @@ claude_mpm/agents/templates/.claude-mpm/memories/README.md,sha256=vEiG7cPjHRZfwX
|
|
|
76
76
|
claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md,sha256=KMZSJrQi-wHOwfl2C0m3A4PpC4QuBtDolAtVybGahKc,77
|
|
77
77
|
claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md,sha256=UBm4BycXtdaa-_l1VCh0alTGGOUSsnCbpKwbFuI-mUY,2219
|
|
78
78
|
claude_mpm/agents/templates/logs/prompts/agent_engineer_20250901_010124_142.md,sha256=oPvFSYFnmJ4TkbTe4AZnNHWaJMJ-xqZP2WM6scUKQKo,13089
|
|
79
|
-
claude_mpm/cli/__init__.py,sha256=
|
|
79
|
+
claude_mpm/cli/__init__.py,sha256=hUzId4sZmUQHvbuxsgbyYetpdhvAq_cMz_uyhXjRLxw,30745
|
|
80
80
|
claude_mpm/cli/__main__.py,sha256=WnVGBwe10InxuZjJRFdwuMF6Gh16aXox6zFgxr0sRXk,847
|
|
81
81
|
claude_mpm/cli/parser.py,sha256=Vqx9n-6Xo1uNhXR4rThmgWpZXTr0nOtkgDf3oMS9b0g,5855
|
|
82
82
|
claude_mpm/cli/startup_logging.py,sha256=RTuyd6CbhiFQz7Z07LDDhK_ZAnZfuJ9B0NghVSntHFI,29390
|
|
83
83
|
claude_mpm/cli/ticket_cli.py,sha256=Cco0riIeo-PuzBslX3-5LAtLUcsLvexSRtVlheb7-cQ,916
|
|
84
|
-
claude_mpm/cli/utils.py,sha256=
|
|
84
|
+
claude_mpm/cli/utils.py,sha256=CZeqOhzMxiOI0odyzqyvN-1VaqlJSdBCSigIZsfoNn4,8786
|
|
85
85
|
claude_mpm/cli/commands/__init__.py,sha256=1EJpBPxZMYQlMUbguqC6QCET8tv6yqY-_FOg814Xxp0,1226
|
|
86
86
|
claude_mpm/cli/commands/agent_manager.py,sha256=PjcUJ83Fhb4LxNCObb1wvh66TtJZLOtbF2lHlebSl64,56126
|
|
87
87
|
claude_mpm/cli/commands/agents.py,sha256=_R0JfewWRNOlf3PhhPkpkhpvCWc3O51kYTbTgn94j4k,59945
|
|
@@ -122,13 +122,13 @@ claude_mpm/cli/commands/upgrade.py,sha256=NYMVONNlj78WHoQ6eyVInroE95AeQxUY2_TpjY
|
|
|
122
122
|
claude_mpm/cli/commands/verify.py,sha256=wmu2UYINK15q2e34TdlTyamvtLDE7r3Oj_XT9zpT5Kk,3687
|
|
123
123
|
claude_mpm/cli/interactive/__init__.py,sha256=vQqUCgPFvLYA1Vkq-5pnY7Ow3A-IgdM0SByfNL1ZLTk,433
|
|
124
124
|
claude_mpm/cli/interactive/agent_wizard.py,sha256=v3nKcLusU49ewjCqvgUWsG35UGc82pC7_Uv-ZzAHZ-c,36201
|
|
125
|
-
claude_mpm/cli/parsers/__init__.py,sha256=
|
|
125
|
+
claude_mpm/cli/parsers/__init__.py,sha256=B-p07lgtefapDHt0SZm7XkSBtXH51uegUWvoDcFtjhw,1084
|
|
126
126
|
claude_mpm/cli/parsers/agent_manager_parser.py,sha256=TQEIm638ELM4X_AAGcn6WrJxlti9wFLfEkHjr-6AtZA,13761
|
|
127
127
|
claude_mpm/cli/parsers/agents_parser.py,sha256=ztzbYFsu4q49MqNKC3oWBPwGPzs5JX4lx9UdDC8h298,9326
|
|
128
128
|
claude_mpm/cli/parsers/analyze_code_parser.py,sha256=cpJSMFbc3mqB4qrMBIEZiikzPekC2IQX-cjt9U2fHW4,5356
|
|
129
129
|
claude_mpm/cli/parsers/analyze_parser.py,sha256=E00Ao0zwzbJPchs_AJt-aoQ7LQEtJPXRCNQ6Piivb4o,3908
|
|
130
130
|
claude_mpm/cli/parsers/auto_configure_parser.py,sha256=NZWZRb00bsGU7_wgiYiPm2ZcPx1E9zy4Gp7MPecQmUA,7264
|
|
131
|
-
claude_mpm/cli/parsers/base_parser.py,sha256=
|
|
131
|
+
claude_mpm/cli/parsers/base_parser.py,sha256=GLnuZW28EXHHX9iiafaiFeC1nm27b55tX8ksr2qe9bg,17939
|
|
132
132
|
claude_mpm/cli/parsers/config_parser.py,sha256=wp6NbV8_p9txP28MXFcQrri0JDIfGFM7u4aJbYJXcYQ,2699
|
|
133
133
|
claude_mpm/cli/parsers/configure_parser.py,sha256=t3cwAQX3BfljDDRJH3i0LplpRprw5jdKcI9Uy3M8xtE,4382
|
|
134
134
|
claude_mpm/cli/parsers/dashboard_parser.py,sha256=JBCM6v_iZhADr_Fwtk_d3up9AOod1avMab-vkNE61gE,3460
|
|
@@ -578,7 +578,7 @@ claude_mpm/services/core/base.py,sha256=iA-F7DgGp-FJIMvQTiHQ68RkG_k-AtUWlArJPMw6
|
|
|
578
578
|
claude_mpm/services/core/cache_manager.py,sha256=dBHvvI6M67kaxFtAubi4IsWfbDZSct1siyKHTmCIrW8,11567
|
|
579
579
|
claude_mpm/services/core/interfaces.py,sha256=FbLhWiOUMlvBfqjYBCeoWgsmRscQGBKteRMW-BGz4hQ,1261
|
|
580
580
|
claude_mpm/services/core/memory_manager.py,sha256=aajBuvBzTq0-EZrjnBjGRdUSaa6MpbfqHAtCePnn7aQ,26258
|
|
581
|
-
claude_mpm/services/core/path_resolver.py,sha256=
|
|
581
|
+
claude_mpm/services/core/path_resolver.py,sha256=11sG6BzIpx4sLSXvGo7wgktz8r-BXSk6qdoaCobn5OI,18370
|
|
582
582
|
claude_mpm/services/core/service_container.py,sha256=3hDwFUahxFZnokPzwgXqBP9swvZhLztKQqwe9xEHgsw,18497
|
|
583
583
|
claude_mpm/services/core/service_interfaces.py,sha256=dlNp0K4gaMcLiNSZxXjsL-kto6vipYw87pBuFK7oVNo,10770
|
|
584
584
|
claude_mpm/services/core/interfaces/__init__.py,sha256=WsJ2ptrqGfLAJhwpmkwJ3GmaIuAalDNcI1IbZEP8E00,9080
|
|
@@ -854,9 +854,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=D0izRT8anjiUb_JG72zlJR_JAw1cDkb7kalN
|
|
|
854
854
|
claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
|
|
855
855
|
claude_mpm/validation/agent_validator.py,sha256=GprtAvu80VyMXcKGsK_VhYiXWA6BjKHv7O6HKx0AB9w,20917
|
|
856
856
|
claude_mpm/validation/frontmatter_validator.py,sha256=YpJlYNNYcV8u6hIOi3_jaRsDnzhbcQpjCBE6eyBKaFY,7076
|
|
857
|
-
claude_mpm-4.14.
|
|
858
|
-
claude_mpm-4.14.
|
|
859
|
-
claude_mpm-4.14.
|
|
860
|
-
claude_mpm-4.14.
|
|
861
|
-
claude_mpm-4.14.
|
|
862
|
-
claude_mpm-4.14.
|
|
857
|
+
claude_mpm-4.14.4.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
|
|
858
|
+
claude_mpm-4.14.4.dist-info/METADATA,sha256=Fw1ZSnYw5oxm0Y1cldnnAtvhSiY12ljdNca59-LwdzM,17967
|
|
859
|
+
claude_mpm-4.14.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
860
|
+
claude_mpm-4.14.4.dist-info/entry_points.txt,sha256=Vlw3GNi-OtTpKSrez04iNrPmxNxYDpIWxmJCxiZ5Tx8,526
|
|
861
|
+
claude_mpm-4.14.4.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
|
862
|
+
claude_mpm-4.14.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|