claude-mpm 5.6.1__py3-none-any.whl → 5.6.76__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 +8 -3
- claude_mpm/auth/__init__.py +35 -0
- claude_mpm/auth/callback_server.py +328 -0
- claude_mpm/auth/models.py +104 -0
- claude_mpm/auth/oauth_manager.py +266 -0
- claude_mpm/auth/providers/__init__.py +12 -0
- claude_mpm/auth/providers/base.py +165 -0
- claude_mpm/auth/providers/google.py +261 -0
- claude_mpm/auth/token_storage.py +252 -0
- claude_mpm/cli/commands/commander.py +174 -4
- claude_mpm/cli/commands/mcp.py +29 -17
- claude_mpm/cli/commands/mcp_command_router.py +39 -0
- claude_mpm/cli/commands/mcp_service_commands.py +304 -0
- claude_mpm/cli/commands/oauth.py +481 -0
- claude_mpm/cli/commands/skill_source.py +51 -2
- claude_mpm/cli/commands/skills.py +5 -3
- claude_mpm/cli/executor.py +9 -0
- claude_mpm/cli/helpers.py +1 -1
- claude_mpm/cli/parsers/base_parser.py +13 -0
- claude_mpm/cli/parsers/commander_parser.py +43 -10
- claude_mpm/cli/parsers/mcp_parser.py +79 -0
- claude_mpm/cli/parsers/oauth_parser.py +165 -0
- claude_mpm/cli/parsers/skill_source_parser.py +4 -0
- claude_mpm/cli/parsers/skills_parser.py +5 -0
- claude_mpm/cli/startup.py +300 -33
- claude_mpm/cli/startup_display.py +4 -2
- claude_mpm/cli/startup_migrations.py +236 -0
- claude_mpm/commander/__init__.py +6 -0
- claude_mpm/commander/adapters/__init__.py +32 -3
- claude_mpm/commander/adapters/auggie.py +260 -0
- claude_mpm/commander/adapters/base.py +98 -1
- claude_mpm/commander/adapters/claude_code.py +32 -1
- claude_mpm/commander/adapters/codex.py +237 -0
- claude_mpm/commander/adapters/example_usage.py +310 -0
- claude_mpm/commander/adapters/mpm.py +389 -0
- claude_mpm/commander/adapters/registry.py +204 -0
- claude_mpm/commander/api/app.py +32 -16
- claude_mpm/commander/api/errors.py +21 -0
- claude_mpm/commander/api/routes/messages.py +11 -11
- claude_mpm/commander/api/routes/projects.py +20 -20
- claude_mpm/commander/api/routes/sessions.py +37 -26
- claude_mpm/commander/api/routes/work.py +86 -50
- claude_mpm/commander/api/schemas.py +4 -0
- claude_mpm/commander/chat/cli.py +47 -5
- claude_mpm/commander/chat/commands.py +44 -16
- claude_mpm/commander/chat/repl.py +1729 -82
- claude_mpm/commander/config.py +5 -3
- claude_mpm/commander/core/__init__.py +10 -0
- claude_mpm/commander/core/block_manager.py +325 -0
- claude_mpm/commander/core/response_manager.py +323 -0
- claude_mpm/commander/daemon.py +215 -10
- claude_mpm/commander/env_loader.py +59 -0
- claude_mpm/commander/events/manager.py +61 -1
- claude_mpm/commander/frameworks/base.py +91 -1
- claude_mpm/commander/frameworks/mpm.py +9 -14
- claude_mpm/commander/git/__init__.py +5 -0
- claude_mpm/commander/git/worktree_manager.py +212 -0
- claude_mpm/commander/instance_manager.py +546 -15
- claude_mpm/commander/memory/__init__.py +45 -0
- claude_mpm/commander/memory/compression.py +347 -0
- claude_mpm/commander/memory/embeddings.py +230 -0
- claude_mpm/commander/memory/entities.py +310 -0
- claude_mpm/commander/memory/example_usage.py +290 -0
- claude_mpm/commander/memory/integration.py +325 -0
- claude_mpm/commander/memory/search.py +381 -0
- claude_mpm/commander/memory/store.py +657 -0
- claude_mpm/commander/models/events.py +6 -0
- claude_mpm/commander/persistence/state_store.py +95 -1
- claude_mpm/commander/registry.py +10 -4
- claude_mpm/commander/runtime/monitor.py +32 -2
- claude_mpm/commander/tmux_orchestrator.py +3 -2
- claude_mpm/commander/work/executor.py +38 -20
- claude_mpm/commander/workflow/event_handler.py +25 -3
- claude_mpm/config/skill_sources.py +16 -0
- claude_mpm/constants.py +5 -0
- claude_mpm/core/claude_runner.py +152 -0
- claude_mpm/core/config.py +30 -22
- claude_mpm/core/config_constants.py +74 -9
- claude_mpm/core/constants.py +56 -12
- claude_mpm/core/hook_manager.py +2 -1
- claude_mpm/core/interactive_session.py +5 -4
- claude_mpm/core/logger.py +16 -2
- claude_mpm/core/logging_utils.py +40 -16
- claude_mpm/core/network_config.py +148 -0
- claude_mpm/core/oneshot_session.py +7 -6
- claude_mpm/core/output_style_manager.py +37 -7
- claude_mpm/core/socketio_pool.py +47 -15
- claude_mpm/core/unified_paths.py +68 -80
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +30 -31
- claude_mpm/hooks/claude_hooks/event_handlers.py +285 -194
- claude_mpm/hooks/claude_hooks/hook_handler.py +115 -32
- claude_mpm/hooks/claude_hooks/installer.py +222 -54
- claude_mpm/hooks/claude_hooks/memory_integration.py +52 -32
- claude_mpm/hooks/claude_hooks/response_tracking.py +40 -59
- claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +25 -30
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +24 -28
- claude_mpm/hooks/claude_hooks/services/container.py +326 -0
- claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +25 -38
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +49 -75
- claude_mpm/hooks/session_resume_hook.py +22 -18
- claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
- claude_mpm/hooks/templates/pre_tool_use_template.py +16 -8
- claude_mpm/init.py +21 -14
- claude_mpm/mcp/__init__.py +9 -0
- claude_mpm/mcp/google_workspace_server.py +610 -0
- claude_mpm/scripts/claude-hook-handler.sh +10 -9
- claude_mpm/services/agents/agent_selection_service.py +2 -2
- claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
- claude_mpm/services/command_deployment_service.py +44 -26
- claude_mpm/services/hook_installer_service.py +77 -8
- claude_mpm/services/mcp_config_manager.py +99 -19
- claude_mpm/services/mcp_service_registry.py +294 -0
- claude_mpm/services/monitor/server.py +6 -1
- claude_mpm/services/pm_skills_deployer.py +5 -3
- claude_mpm/services/skills/git_skill_source_manager.py +79 -8
- claude_mpm/services/skills/selective_skill_deployer.py +28 -0
- claude_mpm/services/skills/skill_discovery_service.py +17 -1
- claude_mpm/services/skills_deployer.py +31 -5
- claude_mpm/skills/__init__.py +2 -1
- claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
- claude_mpm/skills/registry.py +295 -90
- {claude_mpm-5.6.1.dist-info → claude_mpm-5.6.76.dist-info}/METADATA +28 -3
- {claude_mpm-5.6.1.dist-info → claude_mpm-5.6.76.dist-info}/RECORD +131 -93
- {claude_mpm-5.6.1.dist-info → claude_mpm-5.6.76.dist-info}/WHEEL +1 -1
- {claude_mpm-5.6.1.dist-info → claude_mpm-5.6.76.dist-info}/entry_points.txt +2 -0
- {claude_mpm-5.6.1.dist-info → claude_mpm-5.6.76.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.6.1.dist-info → claude_mpm-5.6.76.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.6.1.dist-info → claude_mpm-5.6.76.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""Git worktree manager for session isolation."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import subprocess # nosec B404 - subprocess needed for git commands
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class WorktreeInfo:
|
|
14
|
+
"""Info about a git worktree."""
|
|
15
|
+
|
|
16
|
+
name: str
|
|
17
|
+
path: Path
|
|
18
|
+
branch: str
|
|
19
|
+
base_path: Path # Original repo path
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class WorktreeManager:
|
|
23
|
+
"""Manages git worktrees for session isolation."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, base_path: Path):
|
|
26
|
+
"""Initialize with base project path."""
|
|
27
|
+
self.base_path = Path(base_path)
|
|
28
|
+
self.worktrees_dir = self.base_path.parent / f".worktrees-{self.base_path.name}"
|
|
29
|
+
|
|
30
|
+
def create(self, name: str, branch: Optional[str] = None) -> WorktreeInfo:
|
|
31
|
+
"""Create a worktree for a session.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
name: Session/worktree name (e.g., "izzie")
|
|
35
|
+
branch: Branch name, defaults to session-{name}
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
WorktreeInfo with path and branch details
|
|
39
|
+
"""
|
|
40
|
+
branch = branch or f"session-{name}"
|
|
41
|
+
worktree_path = self.worktrees_dir / name
|
|
42
|
+
|
|
43
|
+
# Ensure worktrees directory exists
|
|
44
|
+
self.worktrees_dir.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
|
|
46
|
+
# Check if worktree already exists
|
|
47
|
+
if worktree_path.exists():
|
|
48
|
+
logger.info(f"Worktree '{name}' already exists at {worktree_path}")
|
|
49
|
+
return self._get_worktree_info(name, worktree_path)
|
|
50
|
+
|
|
51
|
+
# Create new branch if it doesn't exist
|
|
52
|
+
try:
|
|
53
|
+
subprocess.run( # nosec B603 B607 - git command with safe args
|
|
54
|
+
["git", "branch", branch],
|
|
55
|
+
cwd=self.base_path,
|
|
56
|
+
capture_output=True,
|
|
57
|
+
check=False, # Branch may already exist
|
|
58
|
+
)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
logger.warning(f"Could not create branch {branch}: {e}")
|
|
61
|
+
|
|
62
|
+
# Create worktree
|
|
63
|
+
result = subprocess.run( # nosec B603 B607 - git command with safe args
|
|
64
|
+
["git", "worktree", "add", str(worktree_path), branch],
|
|
65
|
+
check=False,
|
|
66
|
+
cwd=self.base_path,
|
|
67
|
+
capture_output=True,
|
|
68
|
+
text=True,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if result.returncode != 0:
|
|
72
|
+
raise RuntimeError(f"Failed to create worktree: {result.stderr}")
|
|
73
|
+
|
|
74
|
+
logger.info(f"Created worktree '{name}' at {worktree_path} on branch {branch}")
|
|
75
|
+
return WorktreeInfo(
|
|
76
|
+
name=name, path=worktree_path, branch=branch, base_path=self.base_path
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def _get_worktree_info(self, name: str, path: Path) -> WorktreeInfo:
|
|
80
|
+
"""Get info for existing worktree."""
|
|
81
|
+
result = subprocess.run( # nosec B603 B607 - git command with safe args
|
|
82
|
+
["git", "branch", "--show-current"],
|
|
83
|
+
check=False,
|
|
84
|
+
cwd=path,
|
|
85
|
+
capture_output=True,
|
|
86
|
+
text=True,
|
|
87
|
+
)
|
|
88
|
+
branch = result.stdout.strip() or f"session-{name}"
|
|
89
|
+
return WorktreeInfo(
|
|
90
|
+
name=name, path=path, branch=branch, base_path=self.base_path
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def list(self) -> list[WorktreeInfo]:
|
|
94
|
+
"""List all worktrees for this project."""
|
|
95
|
+
result = subprocess.run( # nosec B603 B607 - git command with safe args
|
|
96
|
+
["git", "worktree", "list", "--porcelain"],
|
|
97
|
+
check=False,
|
|
98
|
+
cwd=self.base_path,
|
|
99
|
+
capture_output=True,
|
|
100
|
+
text=True,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
worktrees = []
|
|
104
|
+
current_path = None
|
|
105
|
+
current_branch = None
|
|
106
|
+
|
|
107
|
+
for line in result.stdout.strip().split("\n"):
|
|
108
|
+
if line.startswith("worktree "):
|
|
109
|
+
current_path = Path(line.split(" ", 1)[1])
|
|
110
|
+
elif line.startswith("branch "):
|
|
111
|
+
current_branch = line.split("/")[-1] # refs/heads/branch -> branch
|
|
112
|
+
elif line == "":
|
|
113
|
+
if current_path and str(self.worktrees_dir) in str(current_path):
|
|
114
|
+
name = current_path.name
|
|
115
|
+
worktrees.append(
|
|
116
|
+
WorktreeInfo(
|
|
117
|
+
name=name,
|
|
118
|
+
path=current_path,
|
|
119
|
+
branch=current_branch or f"session-{name}",
|
|
120
|
+
base_path=self.base_path,
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
current_path = None
|
|
124
|
+
current_branch = None
|
|
125
|
+
|
|
126
|
+
return worktrees
|
|
127
|
+
|
|
128
|
+
def remove(self, name: str, force: bool = False) -> bool:
|
|
129
|
+
"""Remove a worktree."""
|
|
130
|
+
worktree_path = self.worktrees_dir / name
|
|
131
|
+
|
|
132
|
+
if not worktree_path.exists():
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
args = ["git", "worktree", "remove", str(worktree_path)]
|
|
136
|
+
if force:
|
|
137
|
+
args.insert(3, "--force")
|
|
138
|
+
|
|
139
|
+
result = subprocess.run( # nosec B603 B607 - git command with safe args
|
|
140
|
+
args, check=False, cwd=self.base_path, capture_output=True, text=True
|
|
141
|
+
)
|
|
142
|
+
return result.returncode == 0
|
|
143
|
+
|
|
144
|
+
def get(self, name: str) -> Optional[WorktreeInfo]:
|
|
145
|
+
"""Get worktree by name."""
|
|
146
|
+
worktree_path = self.worktrees_dir / name
|
|
147
|
+
if worktree_path.exists():
|
|
148
|
+
return self._get_worktree_info(name, worktree_path)
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
def merge_to_main(self, name: str, delete_after: bool = True) -> tuple[bool, str]:
|
|
152
|
+
"""Merge worktree branch back to main and optionally delete worktree.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
name: Worktree name to merge
|
|
156
|
+
delete_after: Remove worktree after merge (default True)
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Tuple of (success, message)
|
|
160
|
+
"""
|
|
161
|
+
worktree = self.get(name)
|
|
162
|
+
if not worktree:
|
|
163
|
+
return False, f"Worktree '{name}' not found"
|
|
164
|
+
|
|
165
|
+
# Get main branch name
|
|
166
|
+
result = subprocess.run( # nosec B603 B607 - git command with safe args
|
|
167
|
+
["git", "symbolic-ref", "refs/remotes/origin/HEAD"],
|
|
168
|
+
check=False,
|
|
169
|
+
cwd=self.base_path,
|
|
170
|
+
capture_output=True,
|
|
171
|
+
text=True,
|
|
172
|
+
)
|
|
173
|
+
main_branch = (
|
|
174
|
+
result.stdout.strip().split("/")[-1] if result.returncode == 0 else "main"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# Checkout main in base repo
|
|
178
|
+
checkout_result = subprocess.run( # nosec B603 B607 - git command with safe args
|
|
179
|
+
["git", "checkout", main_branch],
|
|
180
|
+
check=False,
|
|
181
|
+
cwd=self.base_path,
|
|
182
|
+
capture_output=True,
|
|
183
|
+
text=True,
|
|
184
|
+
)
|
|
185
|
+
if checkout_result.returncode != 0:
|
|
186
|
+
return False, f"Failed to checkout {main_branch}: {checkout_result.stderr}"
|
|
187
|
+
|
|
188
|
+
# Merge worktree branch
|
|
189
|
+
merge_result = subprocess.run( # nosec B603 B607 - git command with safe args
|
|
190
|
+
["git", "merge", worktree.branch, "--no-edit"],
|
|
191
|
+
check=False,
|
|
192
|
+
cwd=self.base_path,
|
|
193
|
+
capture_output=True,
|
|
194
|
+
text=True,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
if merge_result.returncode != 0:
|
|
198
|
+
return False, f"Merge failed: {merge_result.stderr}"
|
|
199
|
+
|
|
200
|
+
# Delete worktree if requested
|
|
201
|
+
if delete_after:
|
|
202
|
+
self.remove(name, force=True)
|
|
203
|
+
# Delete branch
|
|
204
|
+
subprocess.run( # nosec B603 B607 - git command with safe args
|
|
205
|
+
["git", "branch", "-d", worktree.branch],
|
|
206
|
+
check=False,
|
|
207
|
+
cwd=self.base_path,
|
|
208
|
+
capture_output=True,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
logger.info(f"Merged '{worktree.branch}' to {main_branch}")
|
|
212
|
+
return True, f"Merged '{worktree.branch}' to {main_branch}"
|