claude-mpm 5.6.3__py3-none-any.whl → 5.6.16__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.
Files changed (118) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/PM_INSTRUCTIONS.md +8 -3
  3. claude_mpm/cli/commands/commander.py +173 -3
  4. claude_mpm/cli/commands/skill_source.py +51 -2
  5. claude_mpm/cli/commands/skills.py +5 -3
  6. claude_mpm/cli/parsers/commander_parser.py +41 -8
  7. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  8. claude_mpm/cli/parsers/skills_parser.py +5 -0
  9. claude_mpm/cli/startup.py +10 -1
  10. claude_mpm/cli/startup_display.py +2 -1
  11. claude_mpm/commander/__init__.py +6 -0
  12. claude_mpm/commander/adapters/__init__.py +32 -3
  13. claude_mpm/commander/adapters/auggie.py +260 -0
  14. claude_mpm/commander/adapters/base.py +98 -1
  15. claude_mpm/commander/adapters/claude_code.py +32 -1
  16. claude_mpm/commander/adapters/codex.py +237 -0
  17. claude_mpm/commander/adapters/example_usage.py +310 -0
  18. claude_mpm/commander/adapters/mpm.py +389 -0
  19. claude_mpm/commander/adapters/registry.py +204 -0
  20. claude_mpm/commander/api/app.py +32 -16
  21. claude_mpm/commander/api/errors.py +21 -0
  22. claude_mpm/commander/api/routes/messages.py +11 -11
  23. claude_mpm/commander/api/routes/projects.py +20 -20
  24. claude_mpm/commander/api/routes/sessions.py +37 -26
  25. claude_mpm/commander/api/routes/work.py +86 -50
  26. claude_mpm/commander/api/schemas.py +4 -0
  27. claude_mpm/commander/chat/cli.py +4 -0
  28. claude_mpm/commander/core/__init__.py +10 -0
  29. claude_mpm/commander/core/block_manager.py +325 -0
  30. claude_mpm/commander/core/response_manager.py +323 -0
  31. claude_mpm/commander/daemon.py +206 -10
  32. claude_mpm/commander/env_loader.py +59 -0
  33. claude_mpm/commander/memory/__init__.py +45 -0
  34. claude_mpm/commander/memory/compression.py +347 -0
  35. claude_mpm/commander/memory/embeddings.py +230 -0
  36. claude_mpm/commander/memory/entities.py +310 -0
  37. claude_mpm/commander/memory/example_usage.py +290 -0
  38. claude_mpm/commander/memory/integration.py +325 -0
  39. claude_mpm/commander/memory/search.py +381 -0
  40. claude_mpm/commander/memory/store.py +657 -0
  41. claude_mpm/commander/registry.py +10 -4
  42. claude_mpm/commander/runtime/monitor.py +32 -2
  43. claude_mpm/commander/work/executor.py +38 -20
  44. claude_mpm/commander/workflow/event_handler.py +25 -3
  45. claude_mpm/config/skill_sources.py +16 -0
  46. claude_mpm/core/claude_runner.py +143 -0
  47. claude_mpm/core/config.py +27 -19
  48. claude_mpm/core/output_style_manager.py +34 -7
  49. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  50. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-312.pyc +0 -0
  51. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  52. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
  53. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc +0 -0
  54. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc +0 -0
  55. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  56. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  57. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
  58. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
  59. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  60. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
  61. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc +0 -0
  62. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  63. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc +0 -0
  64. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  65. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
  66. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc +0 -0
  67. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  68. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
  69. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc +0 -0
  70. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  71. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-312.pyc +0 -0
  72. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc +0 -0
  73. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +29 -30
  74. claude_mpm/hooks/claude_hooks/event_handlers.py +80 -105
  75. claude_mpm/hooks/claude_hooks/hook_handler.py +0 -0
  76. claude_mpm/hooks/claude_hooks/installer.py +41 -0
  77. claude_mpm/hooks/claude_hooks/memory_integration.py +30 -21
  78. claude_mpm/hooks/claude_hooks/response_tracking.py +39 -58
  79. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  80. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-312.pyc +0 -0
  81. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc +0 -0
  82. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  83. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  84. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
  85. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc +0 -0
  86. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  87. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-312.pyc +0 -0
  88. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc +0 -0
  89. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  90. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
  91. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc +0 -0
  92. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  93. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
  94. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc +0 -0
  95. claude_mpm/hooks/claude_hooks/services/connection_manager.py +23 -28
  96. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +22 -26
  97. claude_mpm/hooks/claude_hooks/services/state_manager.py +23 -36
  98. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +47 -73
  99. claude_mpm/hooks/session_resume_hook.py +22 -18
  100. claude_mpm/hooks/templates/pre_tool_use_template.py +10 -2
  101. claude_mpm/scripts/claude-hook-handler.sh +5 -5
  102. claude_mpm/scripts/start_activity_logging.py +0 -0
  103. claude_mpm/services/agents/agent_selection_service.py +2 -2
  104. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  105. claude_mpm/services/skills/git_skill_source_manager.py +79 -8
  106. claude_mpm/services/skills/selective_skill_deployer.py +28 -0
  107. claude_mpm/services/skills/skill_discovery_service.py +17 -1
  108. claude_mpm/services/skills_deployer.py +31 -5
  109. claude_mpm/skills/__init__.py +2 -1
  110. claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
  111. claude_mpm/skills/registry.py +295 -90
  112. {claude_mpm-5.6.3.dist-info → claude_mpm-5.6.16.dist-info}/METADATA +5 -3
  113. {claude_mpm-5.6.3.dist-info → claude_mpm-5.6.16.dist-info}/RECORD +116 -58
  114. {claude_mpm-5.6.3.dist-info → claude_mpm-5.6.16.dist-info}/WHEEL +0 -0
  115. {claude_mpm-5.6.3.dist-info → claude_mpm-5.6.16.dist-info}/entry_points.txt +0 -0
  116. {claude_mpm-5.6.3.dist-info → claude_mpm-5.6.16.dist-info}/licenses/LICENSE +0 -0
  117. {claude_mpm-5.6.3.dist-info → claude_mpm-5.6.16.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  118. {claude_mpm-5.6.3.dist-info → claude_mpm-5.6.16.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION CHANGED
@@ -1 +1 @@
1
- 5.6.3
1
+ 5.6.16
@@ -294,6 +294,8 @@ If you're about to run ANY other command, stop and delegate instead.
294
294
  - Grep (>1), Glob (investigation) → Delegate to research
295
295
  - `mcp__mcp-ticketer__*` → Delegate to ticketing
296
296
  - `mcp__chrome-devtools__*` → Delegate to web-qa
297
+ - `mcp__claude-in-chrome__*` → Delegate to web-qa
298
+ - `mcp__playwright__*` → Delegate to web-qa
297
299
 
298
300
  ## Agent Deployment Architecture
299
301
 
@@ -358,7 +360,7 @@ These are EXAMPLES of routing, not an exhaustive list. **Default to delegation f
358
360
  | **Research** | Understanding codebase, investigating approaches, analyzing files | Grep, Glob, Read multiple files, WebSearch | Investigation tools |
359
361
  | **Engineer** | Writing/modifying code, implementing features, refactoring | Edit, Write, codebase knowledge, testing workflows | - |
360
362
  | **Ops** (local-ops) | Deploying apps, managing infrastructure, starting servers, port/process management | Environment config, deployment procedures | Use `local-ops` for localhost/PM2/docker |
361
- | **QA** (web-qa, api-qa) | Testing implementations, verifying deployments, regression tests, browser testing | Playwright (web), fetch (APIs), verification protocols | For browser: use **web-qa** (never use chrome-devtools directly) |
363
+ | **QA** (web-qa, api-qa) | Testing implementations, verifying deployments, regression tests, browser testing | Playwright (web), fetch (APIs), verification protocols | For browser: use **web-qa** (never use chrome-devtools, claude-in-chrome, or playwright directly) |
362
364
  | **Documentation** | Creating/updating docs, README, API docs, guides | Style consistency, organization standards | - |
363
365
  | **Ticketing** | ALL ticket operations (CRUD, search, hierarchy, comments) | Direct mcp-ticketer access | PM never uses `mcp__mcp-ticketer__*` directly |
364
366
  | **Version Control** | Creating PRs, managing branches, complex git ops | PR workflows, branch management | Check git user for main branch access (bobmatnyc@users.noreply.github.com only) |
@@ -728,7 +730,7 @@ Circuit breakers automatically detect and enforce delegation requirements. All c
728
730
  | 3 | Unverified Assertions | PM claiming status without agent evidence | Require verification evidence | [Details](#circuit-breaker-3-unverified-assertions) |
729
731
  | 4 | File Tracking | PM marking task complete without tracking new files | Run git tracking sequence | [Details](#circuit-breaker-4-file-tracking-enforcement) |
730
732
  | 5 | Delegation Chain | PM claiming completion without full workflow delegation | Execute missing phases | [Details](#circuit-breaker-5-delegation-chain) |
731
- | 6 | Forbidden Tool Usage | PM using ticketing/browser MCP tools directly | Delegate to specialist agent | [Details](#circuit-breaker-6-forbidden-tool-usage) |
733
+ | 6 | Forbidden Tool Usage | PM using ticketing/browser MCP tools (ticketer, chrome-devtools, claude-in-chrome, playwright) directly | Delegate to specialist agent | [Details](#circuit-breaker-6-forbidden-tool-usage) |
732
734
  | 7 | Verification Commands | PM using curl/lsof/ps/wget/nc | Delegate to local-ops or QA | [Details](#circuit-breaker-7-verification-command-detection) |
733
735
  | 8 | QA Verification Gate | PM claiming work complete without QA delegation | BLOCK - Delegate to QA now | [Details](#circuit-breaker-8-qa-verification-gate) |
734
736
  | 9 | User Delegation | PM instructing user to run commands | Delegate to appropriate agent | [Details](#circuit-breaker-9-user-delegation-detection) |
@@ -747,6 +749,9 @@ Circuit breakers automatically detect and enforce delegation requirements. All c
747
749
  - "It works" / "It's deployed" → Circuit Breaker #3
748
750
  - Marks todo complete without `git status` → Circuit Breaker #4
749
751
  - Uses `mcp__mcp-ticketer__*` → Circuit Breaker #6
752
+ - Uses `mcp__chrome-devtools__*` → Circuit Breaker #6
753
+ - Uses `mcp__claude-in-chrome__*` → Circuit Breaker #6
754
+ - Uses `mcp__playwright__*` → Circuit Breaker #6
750
755
  - Uses curl/lsof directly → Circuit Breaker #7
751
756
  - Claims complete without QA → Circuit Breaker #8
752
757
  - "You'll need to run..." → Circuit Breaker #9
@@ -782,7 +787,7 @@ When the user says "just do it" or "handle it", delegate to the full workflow pi
782
787
 
783
788
  When the user says "verify", "check", or "test", delegate to the QA agent with specific verification criteria.
784
789
 
785
- When the user mentions "browser", "screenshot", "click", "navigate", "DOM", "console errors", delegate to web-qa agent for browser testing (NEVER use chrome-devtools tools directly).
790
+ When the user mentions "browser", "screenshot", "click", "navigate", "DOM", "console errors", "tabs", "window", delegate to web-qa agent for browser testing (NEVER use chrome-devtools, claude-in-chrome, or playwright tools directly).
786
791
 
787
792
  When the user mentions "localhost", "local server", or "PM2", delegate to **local-ops** as the primary choice for local development operations.
788
793
 
@@ -2,25 +2,129 @@
2
2
 
3
3
  import asyncio
4
4
  import logging
5
+ import shutil
6
+ import threading
7
+ import time
8
+ from pathlib import Path
5
9
 
6
10
  logger = logging.getLogger(__name__)
7
11
 
12
+ # ANSI colors
13
+ CYAN = "\033[36m"
14
+ DIM = "\033[2m"
15
+ BOLD = "\033[1m"
16
+ YELLOW = "\033[33m"
17
+ GREEN = "\033[32m"
18
+ RED = "\033[31m"
19
+ RESET = "\033[0m"
20
+
21
+
22
+ def _get_terminal_width() -> int:
23
+ """Get terminal width with reasonable bounds."""
24
+ try:
25
+ width = shutil.get_terminal_size().columns
26
+ return max(80, min(width, 120))
27
+ except Exception:
28
+ return 100
29
+
30
+
31
+ def _get_version() -> str:
32
+ """Get Commander version."""
33
+ version_file = Path(__file__).parent.parent.parent / "VERSION"
34
+ if version_file.exists():
35
+ return version_file.read_text().strip()
36
+ return "unknown"
37
+
38
+
39
+ def display_commander_banner():
40
+ """Display Commander-specific startup banner."""
41
+ width = _get_terminal_width()
42
+ version = _get_version()
43
+
44
+ # Commander ASCII art banner
45
+ banner = f"""
46
+ {CYAN}╭{'─' * (width - 2)}╮{RESET}
47
+ {CYAN}│{RESET}{BOLD} ⚡ MPM Commander {RESET}{DIM}v{version}{RESET}{' ' * (width - 24 - len(version))}│
48
+ {CYAN}│{RESET}{DIM} Multi-Project AI Orchestration{RESET}{' ' * (width - 36)}│
49
+ {CYAN}├{'─' * (width - 2)}┤{RESET}
50
+ {CYAN}│{RESET} {YELLOW}ALPHA{RESET} - APIs may change {' ' * (width - 55)}│
51
+ {CYAN}╰{'─' * (width - 2)}╯{RESET}
52
+ """
53
+ print(banner)
54
+
55
+
56
+ def _count_cached_agents() -> int:
57
+ """Count cached agents from ~/.claude-mpm/cache/agents/."""
58
+ try:
59
+ cache_agents_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
60
+ if not cache_agents_dir.exists():
61
+ return 0
62
+ # Recursively find all .md files excluding base/README files
63
+ agent_files = [
64
+ f
65
+ for f in cache_agents_dir.rglob("*.md")
66
+ if f.is_file()
67
+ and not f.name.startswith(".")
68
+ and f.name not in ("README.md", "BASE-AGENT.md", "INSTRUCTIONS.md")
69
+ ]
70
+ return len(agent_files)
71
+ except Exception:
72
+ return 0
73
+
74
+
75
+ def _count_cached_skills() -> int:
76
+ """Count cached skills from ~/.claude-mpm/cache/skills/."""
77
+ try:
78
+ cache_skills_dir = Path.home() / ".claude-mpm" / "cache" / "skills"
79
+ if not cache_skills_dir.exists():
80
+ return 0
81
+ # Recursively find all directories containing SKILL.md
82
+ skill_files = list(cache_skills_dir.rglob("SKILL.md"))
83
+ return len(skill_files)
84
+ except Exception:
85
+ return 0
86
+
87
+
88
+ def load_agents_and_skills():
89
+ """Load agents and skills for Commander sessions."""
90
+ try:
91
+ print(f"{DIM}Loading agents...{RESET}", end=" ", flush=True)
92
+ agent_count = _count_cached_agents()
93
+ print(f"{GREEN}✓{RESET} {agent_count} agents")
94
+
95
+ print(f"{DIM}Loading skills...{RESET}", end=" ", flush=True)
96
+ skill_count = _count_cached_skills()
97
+ print(f"{GREEN}✓{RESET} {skill_count} skills")
98
+
99
+ return agent_count, skill_count
100
+ except Exception as e:
101
+ logger.warning(f"Could not load agents/skills: {e}")
102
+ print(f"{YELLOW}⚠{RESET} Could not load agents/skills")
103
+ return 0, 0
104
+
8
105
 
9
106
  def handle_commander_command(args) -> int:
10
- """Handle the commander command.
107
+ """Handle the commander command with auto-starting daemon.
11
108
 
12
109
  Args:
13
110
  args: Parsed command line arguments with:
14
- - port: Port for internal services (default: 8765)
111
+ - port: Port for daemon (default: 8765)
112
+ - host: Host for daemon (default: 127.0.0.1)
15
113
  - state_dir: Optional state directory path
16
114
  - debug: Enable debug logging
115
+ - no_chat: Start daemon only without interactive chat
116
+ - daemon_only: Alias for no_chat
17
117
 
18
118
  Returns:
19
119
  Exit code (0 for success, 1 for error)
20
120
  """
21
121
  try:
22
122
  # Import here to avoid circular dependencies
123
+ import requests
124
+
23
125
  from claude_mpm.commander.chat.cli import run_commander
126
+ from claude_mpm.commander.config import DaemonConfig
127
+ from claude_mpm.commander.daemon import main as daemon_main
24
128
 
25
129
  # Setup debug logging if requested
26
130
  if getattr(args, "debug", False):
@@ -29,11 +133,76 @@ def handle_commander_command(args) -> int:
29
133
  format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
30
134
  )
31
135
 
136
+ # Display Commander banner
137
+ display_commander_banner()
138
+
139
+ # Load agents and skills
140
+ load_agents_and_skills()
141
+
142
+ print() # Blank line after loading
143
+
32
144
  # Get arguments
33
145
  port = getattr(args, "port", 8765)
146
+ host = getattr(args, "host", "127.0.0.1")
34
147
  state_dir = getattr(args, "state_dir", None)
148
+ no_chat = getattr(args, "no_chat", False) or getattr(args, "daemon_only", False)
149
+
150
+ # Check if daemon already running
151
+ daemon_running = False
152
+ try:
153
+ resp = requests.get(f"http://{host}:{port}/api/health", timeout=1)
154
+ if resp.status_code == 200:
155
+ print(f"{GREEN}✓{RESET} Daemon already running on {host}:{port}")
156
+ daemon_running = True
157
+ except (requests.RequestException, requests.ConnectionError):
158
+ pass
159
+
160
+ # Start daemon if not running
161
+ if not daemon_running:
162
+ print(
163
+ f"{DIM}Starting daemon on {host}:{port}...{RESET}", end=" ", flush=True
164
+ )
165
+
166
+ # Create daemon config
167
+ config_kwargs = {"host": host, "port": port}
168
+ if state_dir:
169
+ config_kwargs["state_dir"] = state_dir
170
+ config = DaemonConfig(**config_kwargs)
171
+
172
+ # Start daemon in background thread
173
+ daemon_thread = threading.Thread(
174
+ target=lambda: asyncio.run(daemon_main(config)), daemon=True
175
+ )
176
+ daemon_thread.start()
177
+
178
+ # Wait for daemon to be ready (max 3 seconds)
179
+ for _ in range(30):
180
+ time.sleep(0.1)
181
+ try:
182
+ resp = requests.get(f"http://{host}:{port}/api/health", timeout=1)
183
+ if resp.status_code == 200:
184
+ print(f"{GREEN}✓{RESET}")
185
+ daemon_running = True
186
+ break
187
+ except (requests.RequestException, requests.ConnectionError):
188
+ pass
189
+ else:
190
+ print(f"{RED}✗{RESET} Failed (timeout)")
191
+ return 1
192
+
193
+ # If daemon-only mode, keep running until interrupted
194
+ if no_chat:
195
+ print(f"\n{CYAN}Daemon running.{RESET} API at http://{host}:{port}")
196
+ print(f"{DIM}Press Ctrl+C to stop{RESET}\n")
197
+ try:
198
+ while True:
199
+ time.sleep(1)
200
+ except KeyboardInterrupt:
201
+ print(f"\n{DIM}Shutting down...{RESET}")
202
+ return 0
35
203
 
36
- # Run commander
204
+ # Launch interactive chat
205
+ print(f"\n{CYAN}Entering Commander chat...{RESET}\n")
37
206
  asyncio.run(run_commander(port=port, state_dir=state_dir))
38
207
 
39
208
  return 0
@@ -43,4 +212,5 @@ def handle_commander_command(args) -> int:
43
212
  return 0
44
213
  except Exception as e:
45
214
  logger.error(f"Commander error: {e}", exc_info=True)
215
+ print(f"{RED}Error:{RESET} {e}")
46
216
  return 1
@@ -11,6 +11,7 @@ for better UX. Handles errors gracefully with actionable messages.
11
11
 
12
12
  import json
13
13
  import logging
14
+ import os
14
15
  import re
15
16
 
16
17
  from ...config.skill_sources import SkillSource, SkillSourceConfiguration
@@ -20,6 +21,33 @@ from ...services.skills.skill_discovery_service import SkillDiscoveryService
20
21
  logger = logging.getLogger(__name__)
21
22
 
22
23
 
24
+ def _get_github_token(source: SkillSource | None = None) -> str | None:
25
+ """Get GitHub token with source-specific override support.
26
+
27
+ Priority: source.token > GITHUB_TOKEN > GH_TOKEN
28
+
29
+ Args:
30
+ source: Optional SkillSource to check for per-source token
31
+
32
+ Returns:
33
+ GitHub token if found, None otherwise
34
+
35
+ Security Note:
36
+ Token is never logged or printed to avoid exposure.
37
+ """
38
+ # Priority 1: Per-source token (env var reference or direct)
39
+ if source and source.token:
40
+ if source.token.startswith("$"):
41
+ # Env var reference: $VAR_NAME -> os.environ.get("VAR_NAME")
42
+ env_var_name = source.token[1:]
43
+ return os.environ.get(env_var_name)
44
+ # Direct token (not recommended but supported)
45
+ return source.token
46
+
47
+ # Priority 2-3: Global environment variables
48
+ return os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN")
49
+
50
+
23
51
  def _test_skill_repository_access(source: SkillSource) -> dict:
24
52
  """Test if skill repository is accessible via GitHub API.
25
53
 
@@ -58,7 +86,13 @@ def _test_skill_repository_access(source: SkillSource) -> dict:
58
86
  # Test GitHub API access
59
87
  api_url = f"https://api.github.com/repos/{owner_repo}"
60
88
 
61
- response = requests.get(api_url, timeout=10)
89
+ # Build headers with authentication if token available
90
+ headers = {"Accept": "application/vnd.github+json"}
91
+ token = _get_github_token(source)
92
+ if token:
93
+ headers["Authorization"] = f"token {token}"
94
+
95
+ response = requests.get(api_url, headers=headers, timeout=10)
62
96
 
63
97
  if response.status_code == 200:
64
98
  return {"accessible": True, "error": None}
@@ -68,9 +102,14 @@ def _test_skill_repository_access(source: SkillSource) -> dict:
68
102
  "error": f"Repository not found: {owner_repo}",
69
103
  }
70
104
  if response.status_code == 403:
105
+ error_msg = "Access denied (private repository or rate limit)"
106
+ if not token:
107
+ error_msg += (
108
+ ". Try setting GITHUB_TOKEN environment variable for private repos"
109
+ )
71
110
  return {
72
111
  "accessible": False,
73
- "error": "Access denied (private repository or rate limit)",
112
+ "error": error_msg,
74
113
  }
75
114
  return {
76
115
  "accessible": False,
@@ -263,6 +302,15 @@ def handle_add_skill_source(args) -> int:
263
302
 
264
303
  # Create new source
265
304
  enabled = not args.disabled
305
+ token = getattr(args, "token", None)
306
+
307
+ # Security warning for direct tokens
308
+ if token and not token.startswith("$"):
309
+ print("⚠️ Warning: Direct token values in config are not recommended")
310
+ print(" Consider using environment variable reference instead:")
311
+ print(" --token $MY_PRIVATE_TOKEN")
312
+ print()
313
+
266
314
  source = SkillSource(
267
315
  id=source_id,
268
316
  type="git",
@@ -270,6 +318,7 @@ def handle_add_skill_source(args) -> int:
270
318
  branch=args.branch,
271
319
  priority=args.priority,
272
320
  enabled=enabled,
321
+ token=token,
273
322
  )
274
323
 
275
324
  # Determine if we should test
@@ -538,6 +538,7 @@ class SkillsManagementCommand(BaseCommand):
538
538
  toolchain = getattr(args, "toolchain", None)
539
539
  categories = getattr(args, "categories", None)
540
540
  force = getattr(args, "force", False)
541
+ deploy_all = getattr(args, "all", False)
541
542
 
542
543
  if collection:
543
544
  console.print(
@@ -548,14 +549,15 @@ class SkillsManagementCommand(BaseCommand):
548
549
  "\n[bold cyan]Deploying skills from default collection...[/bold cyan]\n"
549
550
  )
550
551
 
551
- # Selective deployment is ALWAYS enabled (deploy only agent-referenced skills)
552
- # This ensures only skills linked to deployed agents are deployed
552
+ # Use selective deployment unless --all flag is provided
553
+ # Selective mode deploys only agent-referenced skills
554
+ # --all mode deploys all available skills from the collection
553
555
  result = self.skills_deployer.deploy_skills(
554
556
  collection=collection,
555
557
  toolchain=toolchain,
556
558
  categories=categories,
557
559
  force=force,
558
- selective=True, # Always use selective deployment
560
+ selective=not deploy_all,
559
561
  )
560
562
 
561
563
  # Display results
@@ -23,17 +23,21 @@ def add_commander_subparser(subparsers: argparse._SubParsersAction) -> None:
23
23
  """
24
24
  commander_parser = subparsers.add_parser(
25
25
  "commander",
26
- help="Interactive Commander mode for managing multiple Claude instances",
26
+ help="Launch Commander multi-project orchestration (ALPHA)",
27
27
  description="""
28
- Commander Mode - Interactive Instance Management
28
+ Commander Mode - Multi-Project Orchestration (ALPHA)
29
29
 
30
- Commander provides an interactive REPL interface for:
31
- - Starting and stopping Claude Code/MPM instances in tmux
32
- - Connecting to instances and sending natural language commands
33
- - Managing multiple concurrent projects
34
- - Viewing instance status and output
30
+ The commander subcommand auto-starts the Commander daemon (if not already running)
31
+ and launches an interactive REPL for managing multiple Claude Code instances.
35
32
 
36
- Commands:
33
+ Commander provides:
34
+ - Auto-starting daemon that manages project lifecycles
35
+ - Interactive REPL for controlling instances
36
+ - Tmux-based session management
37
+ - Real-time output monitoring
38
+ - REST API for external control (http://127.0.0.1:8765)
39
+
40
+ REPL Commands:
37
41
  list, ls, instances List active instances
38
42
  start <path> Start new instance at path
39
43
  --framework <cc|mpm> Specify framework (default: cc)
@@ -50,7 +54,16 @@ Natural Language:
50
54
  command will be sent to the connected instance as a message.
51
55
 
52
56
  Examples:
57
+ # Start daemon and launch interactive chat
53
58
  claude-mpm commander
59
+
60
+ # Start daemon only (no chat interface)
61
+ claude-mpm commander --daemon-only
62
+
63
+ # Use custom port
64
+ claude-mpm commander --port 9000
65
+
66
+ # In REPL:
54
67
  > start ~/myproject --framework cc --name myapp
55
68
  > connect myapp
56
69
  > Fix the authentication bug in login.py
@@ -81,3 +94,23 @@ Examples:
81
94
  action="store_true",
82
95
  help="Enable debug logging",
83
96
  )
97
+
98
+ # Daemon auto-start options
99
+ commander_parser.add_argument(
100
+ "--host",
101
+ type=str,
102
+ default="127.0.0.1",
103
+ help="Daemon host (default: 127.0.0.1)",
104
+ )
105
+
106
+ commander_parser.add_argument(
107
+ "--no-chat",
108
+ action="store_true",
109
+ help="Start daemon only without interactive chat",
110
+ )
111
+
112
+ commander_parser.add_argument(
113
+ "--daemon-only",
114
+ action="store_true",
115
+ help="Alias for --no-chat (start daemon only)",
116
+ )
@@ -76,6 +76,10 @@ def add_skill_source_subparser(subparsers) -> argparse.ArgumentParser:
76
76
  dest="skip_test",
77
77
  help="Skip immediate testing (not recommended)",
78
78
  )
79
+ add_parser.add_argument(
80
+ "--token",
81
+ help="GitHub token or env var reference (e.g., ghp_xxx or $PRIVATE_TOKEN)",
82
+ )
79
83
 
80
84
  # Remove repository
81
85
  remove_parser = skill_source_subparsers.add_parser(
@@ -167,6 +167,11 @@ def add_skills_subparser(subparsers) -> argparse.ArgumentParser:
167
167
  action="store_true",
168
168
  help="Force redeployment of already deployed skills",
169
169
  )
170
+ deploy_github_parser.add_argument(
171
+ "--all",
172
+ action="store_true",
173
+ help="Deploy all available skills, not just agent-referenced ones",
174
+ )
170
175
 
171
176
  # List available GitHub skills
172
177
  list_available_parser = skills_subparsers.add_parser(
claude_mpm/cli/startup.py CHANGED
@@ -210,7 +210,16 @@ def should_skip_background_services(args, processed_argv):
210
210
  return any(cmd in (processed_argv or sys.argv[1:]) for cmd in skip_commands) or (
211
211
  hasattr(args, "command")
212
212
  and args.command
213
- in ["info", "doctor", "config", "mcp", "configure", "hook-errors", "autotodos"]
213
+ in [
214
+ "info",
215
+ "doctor",
216
+ "config",
217
+ "mcp",
218
+ "configure",
219
+ "hook-errors",
220
+ "autotodos",
221
+ "commander",
222
+ ]
214
223
  )
215
224
 
216
225
 
@@ -540,7 +540,8 @@ def should_show_banner(args) -> bool:
540
540
  return False
541
541
 
542
542
  # Check for commands that should skip banner
543
- skip_commands = {"info", "doctor", "config", "configure"}
543
+ # Commander has its own banner, so skip the main MPM banner
544
+ skip_commands = {"info", "doctor", "config", "configure", "commander"}
544
545
  if hasattr(args, "command") and args.command in skip_commands:
545
546
  return False
546
547
 
@@ -12,12 +12,18 @@ Key Components:
12
12
  - ProjectSession: Per-project lifecycle management
13
13
  - InstanceManager: Framework selection and instance lifecycle
14
14
  - Frameworks: Claude Code, MPM framework abstractions
15
+ - Memory: Conversation storage, semantic search, context compression
15
16
 
16
17
  Example:
17
18
  >>> from claude_mpm.commander import ProjectRegistry
18
19
  >>> registry = ProjectRegistry()
19
20
  >>> project = registry.register("/path/to/project")
20
21
  >>> registry.update_state(project.id, ProjectState.WORKING)
22
+
23
+ >>> # Memory integration
24
+ >>> from claude_mpm.commander.memory import MemoryIntegration
25
+ >>> memory = MemoryIntegration.create()
26
+ >>> await memory.capture_project_conversation(project)
21
27
  """
22
28
 
23
29
  from claude_mpm.commander.config import DaemonConfig
@@ -6,26 +6,55 @@ the TmuxOrchestrator to interface with various runtimes in a uniform way.
6
6
  Two types of adapters:
7
7
  - RuntimeAdapter: Synchronous parsing and state detection
8
8
  - CommunicationAdapter: Async I/O and state management
9
+
10
+ Available Runtime Adapters:
11
+ - ClaudeCodeAdapter: Vanilla Claude Code CLI
12
+ - AuggieAdapter: Auggie with MCP support
13
+ - CodexAdapter: Codex (limited features)
14
+ - MPMAdapter: Full MPM with agents, hooks, skills, monitoring
15
+
16
+ Registry:
17
+ - AdapterRegistry: Centralized adapter management with auto-detection
9
18
  """
10
19
 
11
- from .base import Capability, ParsedResponse, RuntimeAdapter
20
+ from .auggie import AuggieAdapter
21
+ from .base import (
22
+ Capability,
23
+ ParsedResponse,
24
+ RuntimeAdapter,
25
+ RuntimeCapability,
26
+ RuntimeInfo,
27
+ )
12
28
  from .claude_code import ClaudeCodeAdapter
29
+ from .codex import CodexAdapter
13
30
  from .communication import (
14
31
  AdapterResponse,
15
32
  AdapterState,
16
33
  BaseCommunicationAdapter,
17
34
  ClaudeCodeCommunicationAdapter,
18
35
  )
36
+ from .mpm import MPMAdapter
37
+ from .registry import AdapterRegistry
38
+
39
+ # Auto-register all adapters
40
+ AdapterRegistry.register("claude-code", ClaudeCodeAdapter)
41
+ AdapterRegistry.register("auggie", AuggieAdapter)
42
+ AdapterRegistry.register("codex", CodexAdapter)
43
+ AdapterRegistry.register("mpm", MPMAdapter)
19
44
 
20
45
  __all__ = [
21
- # Communication adapters (async I/O)
46
+ "AdapterRegistry",
22
47
  "AdapterResponse",
23
48
  "AdapterState",
49
+ "AuggieAdapter",
24
50
  "BaseCommunicationAdapter",
25
- # Runtime adapters (parsing)
26
51
  "Capability",
27
52
  "ClaudeCodeAdapter",
28
53
  "ClaudeCodeCommunicationAdapter",
54
+ "CodexAdapter",
55
+ "MPMAdapter",
29
56
  "ParsedResponse",
30
57
  "RuntimeAdapter",
58
+ "RuntimeCapability",
59
+ "RuntimeInfo",
31
60
  ]