claude-mpm 5.4.96__py3-none-any.whl → 5.6.17__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.

Files changed (214) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/{CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md → CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md} +14 -6
  3. claude_mpm/agents/PM_INSTRUCTIONS.md +44 -10
  4. claude_mpm/agents/WORKFLOW.md +2 -0
  5. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  6. claude_mpm/cli/commands/autotodos.py +45 -5
  7. claude_mpm/cli/commands/commander.py +216 -0
  8. claude_mpm/cli/commands/hook_errors.py +60 -60
  9. claude_mpm/cli/commands/run.py +35 -3
  10. claude_mpm/cli/commands/skill_source.py +51 -2
  11. claude_mpm/cli/commands/skills.py +5 -3
  12. claude_mpm/cli/executor.py +32 -17
  13. claude_mpm/cli/parsers/base_parser.py +17 -0
  14. claude_mpm/cli/parsers/commander_parser.py +116 -0
  15. claude_mpm/cli/parsers/run_parser.py +10 -0
  16. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  17. claude_mpm/cli/parsers/skills_parser.py +5 -0
  18. claude_mpm/cli/startup.py +124 -3
  19. claude_mpm/cli/startup_display.py +2 -1
  20. claude_mpm/cli/utils.py +7 -3
  21. claude_mpm/commander/__init__.py +78 -0
  22. claude_mpm/commander/adapters/__init__.py +60 -0
  23. claude_mpm/commander/adapters/auggie.py +260 -0
  24. claude_mpm/commander/adapters/base.py +288 -0
  25. claude_mpm/commander/adapters/claude_code.py +392 -0
  26. claude_mpm/commander/adapters/codex.py +237 -0
  27. claude_mpm/commander/adapters/communication.py +366 -0
  28. claude_mpm/commander/adapters/example_usage.py +310 -0
  29. claude_mpm/commander/adapters/mpm.py +389 -0
  30. claude_mpm/commander/adapters/registry.py +204 -0
  31. claude_mpm/commander/api/__init__.py +16 -0
  32. claude_mpm/commander/api/app.py +121 -0
  33. claude_mpm/commander/api/errors.py +133 -0
  34. claude_mpm/commander/api/routes/__init__.py +8 -0
  35. claude_mpm/commander/api/routes/events.py +184 -0
  36. claude_mpm/commander/api/routes/inbox.py +171 -0
  37. claude_mpm/commander/api/routes/messages.py +148 -0
  38. claude_mpm/commander/api/routes/projects.py +271 -0
  39. claude_mpm/commander/api/routes/sessions.py +226 -0
  40. claude_mpm/commander/api/routes/work.py +296 -0
  41. claude_mpm/commander/api/schemas.py +186 -0
  42. claude_mpm/commander/chat/__init__.py +7 -0
  43. claude_mpm/commander/chat/cli.py +111 -0
  44. claude_mpm/commander/chat/commands.py +96 -0
  45. claude_mpm/commander/chat/repl.py +310 -0
  46. claude_mpm/commander/config.py +49 -0
  47. claude_mpm/commander/config_loader.py +115 -0
  48. claude_mpm/commander/core/__init__.py +10 -0
  49. claude_mpm/commander/core/block_manager.py +325 -0
  50. claude_mpm/commander/core/response_manager.py +323 -0
  51. claude_mpm/commander/daemon.py +594 -0
  52. claude_mpm/commander/env_loader.py +59 -0
  53. claude_mpm/commander/events/__init__.py +26 -0
  54. claude_mpm/commander/events/manager.py +332 -0
  55. claude_mpm/commander/frameworks/__init__.py +12 -0
  56. claude_mpm/commander/frameworks/base.py +143 -0
  57. claude_mpm/commander/frameworks/claude_code.py +58 -0
  58. claude_mpm/commander/frameworks/mpm.py +62 -0
  59. claude_mpm/commander/inbox/__init__.py +16 -0
  60. claude_mpm/commander/inbox/dedup.py +128 -0
  61. claude_mpm/commander/inbox/inbox.py +224 -0
  62. claude_mpm/commander/inbox/models.py +70 -0
  63. claude_mpm/commander/instance_manager.py +337 -0
  64. claude_mpm/commander/llm/__init__.py +6 -0
  65. claude_mpm/commander/llm/openrouter_client.py +167 -0
  66. claude_mpm/commander/llm/summarizer.py +70 -0
  67. claude_mpm/commander/memory/__init__.py +45 -0
  68. claude_mpm/commander/memory/compression.py +347 -0
  69. claude_mpm/commander/memory/embeddings.py +230 -0
  70. claude_mpm/commander/memory/entities.py +310 -0
  71. claude_mpm/commander/memory/example_usage.py +290 -0
  72. claude_mpm/commander/memory/integration.py +325 -0
  73. claude_mpm/commander/memory/search.py +381 -0
  74. claude_mpm/commander/memory/store.py +657 -0
  75. claude_mpm/commander/models/__init__.py +18 -0
  76. claude_mpm/commander/models/events.py +121 -0
  77. claude_mpm/commander/models/project.py +162 -0
  78. claude_mpm/commander/models/work.py +214 -0
  79. claude_mpm/commander/parsing/__init__.py +20 -0
  80. claude_mpm/commander/parsing/extractor.py +132 -0
  81. claude_mpm/commander/parsing/output_parser.py +270 -0
  82. claude_mpm/commander/parsing/patterns.py +100 -0
  83. claude_mpm/commander/persistence/__init__.py +11 -0
  84. claude_mpm/commander/persistence/event_store.py +274 -0
  85. claude_mpm/commander/persistence/state_store.py +309 -0
  86. claude_mpm/commander/persistence/work_store.py +164 -0
  87. claude_mpm/commander/polling/__init__.py +13 -0
  88. claude_mpm/commander/polling/event_detector.py +104 -0
  89. claude_mpm/commander/polling/output_buffer.py +49 -0
  90. claude_mpm/commander/polling/output_poller.py +153 -0
  91. claude_mpm/commander/project_session.py +268 -0
  92. claude_mpm/commander/proxy/__init__.py +12 -0
  93. claude_mpm/commander/proxy/formatter.py +89 -0
  94. claude_mpm/commander/proxy/output_handler.py +191 -0
  95. claude_mpm/commander/proxy/relay.py +155 -0
  96. claude_mpm/commander/registry.py +410 -0
  97. claude_mpm/commander/runtime/__init__.py +10 -0
  98. claude_mpm/commander/runtime/executor.py +191 -0
  99. claude_mpm/commander/runtime/monitor.py +346 -0
  100. claude_mpm/commander/session/__init__.py +6 -0
  101. claude_mpm/commander/session/context.py +81 -0
  102. claude_mpm/commander/session/manager.py +59 -0
  103. claude_mpm/commander/tmux_orchestrator.py +361 -0
  104. claude_mpm/commander/web/__init__.py +1 -0
  105. claude_mpm/commander/work/__init__.py +30 -0
  106. claude_mpm/commander/work/executor.py +207 -0
  107. claude_mpm/commander/work/queue.py +405 -0
  108. claude_mpm/commander/workflow/__init__.py +27 -0
  109. claude_mpm/commander/workflow/event_handler.py +241 -0
  110. claude_mpm/commander/workflow/notifier.py +146 -0
  111. claude_mpm/commands/mpm-config.md +8 -0
  112. claude_mpm/commands/mpm-doctor.md +8 -0
  113. claude_mpm/commands/mpm-help.md +8 -0
  114. claude_mpm/commands/mpm-init.md +8 -0
  115. claude_mpm/commands/mpm-monitor.md +8 -0
  116. claude_mpm/commands/mpm-organize.md +8 -0
  117. claude_mpm/commands/mpm-postmortem.md +8 -0
  118. claude_mpm/commands/mpm-session-resume.md +8 -0
  119. claude_mpm/commands/mpm-status.md +8 -0
  120. claude_mpm/commands/mpm-ticket-view.md +8 -0
  121. claude_mpm/commands/mpm-version.md +8 -0
  122. claude_mpm/commands/mpm.md +8 -0
  123. claude_mpm/config/agent_presets.py +8 -7
  124. claude_mpm/config/skill_sources.py +16 -0
  125. claude_mpm/core/claude_runner.py +143 -0
  126. claude_mpm/core/config.py +32 -19
  127. claude_mpm/core/logger.py +26 -9
  128. claude_mpm/core/logging_utils.py +35 -11
  129. claude_mpm/core/output_style_manager.py +49 -12
  130. claude_mpm/core/unified_config.py +10 -6
  131. claude_mpm/core/unified_paths.py +68 -80
  132. claude_mpm/experimental/cli_enhancements.py +2 -1
  133. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-312.pyc +0 -0
  134. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  135. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
  136. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc +0 -0
  137. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc +0 -0
  138. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  139. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
  140. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
  141. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  142. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
  143. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc +0 -0
  144. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  145. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc +0 -0
  146. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  147. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
  148. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc +0 -0
  149. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  150. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
  151. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc +0 -0
  152. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-312.pyc +0 -0
  153. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc +0 -0
  154. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +29 -30
  155. claude_mpm/hooks/claude_hooks/event_handlers.py +112 -99
  156. claude_mpm/hooks/claude_hooks/hook_handler.py +81 -88
  157. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  158. claude_mpm/hooks/claude_hooks/installer.py +116 -8
  159. claude_mpm/hooks/claude_hooks/memory_integration.py +51 -31
  160. claude_mpm/hooks/claude_hooks/response_tracking.py +39 -58
  161. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-312.pyc +0 -0
  162. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.pyc +0 -0
  163. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  164. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  165. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
  166. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc +0 -0
  167. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-312.pyc +0 -0
  168. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc +0 -0
  169. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  170. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
  171. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc +0 -0
  172. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  173. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
  174. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc +0 -0
  175. claude_mpm/hooks/claude_hooks/services/connection_manager.py +23 -28
  176. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +36 -103
  177. claude_mpm/hooks/claude_hooks/services/state_manager.py +23 -36
  178. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +47 -73
  179. claude_mpm/hooks/session_resume_hook.py +22 -18
  180. claude_mpm/hooks/templates/pre_tool_use_template.py +10 -2
  181. claude_mpm/scripts/claude-hook-handler.sh +43 -16
  182. claude_mpm/scripts/start_activity_logging.py +0 -0
  183. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  184. claude_mpm/services/agents/agent_selection_service.py +2 -2
  185. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  186. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  187. claude_mpm/services/event_log.py +8 -0
  188. claude_mpm/services/pm_skills_deployer.py +84 -6
  189. claude_mpm/services/skills/git_skill_source_manager.py +130 -10
  190. claude_mpm/services/skills/selective_skill_deployer.py +28 -0
  191. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  192. claude_mpm/services/skills_deployer.py +31 -5
  193. claude_mpm/skills/__init__.py +2 -1
  194. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  195. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  196. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  197. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  198. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  199. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  200. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  201. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  202. claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
  203. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  204. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  205. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  206. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  207. claude_mpm/skills/registry.py +295 -90
  208. {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.17.dist-info}/METADATA +22 -6
  209. {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.17.dist-info}/RECORD +213 -83
  210. {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.17.dist-info}/WHEEL +0 -0
  211. {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.17.dist-info}/entry_points.txt +0 -0
  212. {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.17.dist-info}/licenses/LICENSE +0 -0
  213. {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.17.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  214. {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.17.dist-info}/top_level.txt +0 -0
claude_mpm/cli/startup.py CHANGED
@@ -161,7 +161,25 @@ def setup_early_environment(argv):
161
161
  # CRITICAL: Suppress ALL logging by default
162
162
  # This catches all loggers (claude_mpm.*, service.*, framework_loader, etc.)
163
163
  # This will be overridden by setup_mcp_server_logging() based on user preference
164
- logging.getLogger().setLevel(logging.CRITICAL + 1) # Root logger catches everything
164
+ root_logger = logging.getLogger()
165
+ root_logger.setLevel(logging.CRITICAL + 1) # Root logger catches everything
166
+ root_logger.handlers = [] # Remove any handlers
167
+
168
+ # Also suppress common module loggers explicitly to prevent handler leakage
169
+ for logger_name in [
170
+ "claude_mpm",
171
+ "path_resolver",
172
+ "file_loader",
173
+ "framework_loader",
174
+ "service",
175
+ "instruction_loader",
176
+ "agent_loader",
177
+ "startup",
178
+ ]:
179
+ module_logger = logging.getLogger(logger_name)
180
+ module_logger.setLevel(logging.CRITICAL + 1)
181
+ module_logger.handlers = []
182
+ module_logger.propagate = False
165
183
 
166
184
  # Process argv
167
185
  if argv is None:
@@ -192,7 +210,16 @@ def should_skip_background_services(args, processed_argv):
192
210
  return any(cmd in (processed_argv or sys.argv[1:]) for cmd in skip_commands) or (
193
211
  hasattr(args, "command")
194
212
  and args.command
195
- 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
+ ]
196
223
  )
197
224
 
198
225
 
@@ -316,7 +343,7 @@ def deploy_output_style_on_startup():
316
343
  Deploys all styles:
317
344
  - claude-mpm.md (professional mode)
318
345
  - claude-mpm-teacher.md (teaching mode)
319
- - claude-mpm-founders.md (founders mode)
346
+ - claude-mpm-research.md (research mode - for codebase analysis)
320
347
  """
321
348
  try:
322
349
  from ..core.output_style_manager import OutputStyleManager
@@ -451,6 +478,94 @@ def _cleanup_orphaned_agents(deploy_target: Path, deployed_agents: list[str]) ->
451
478
  return removed_count
452
479
 
453
480
 
481
+ def _save_deployment_state_after_reconciliation(
482
+ agent_result, project_path: Path
483
+ ) -> None:
484
+ """Save deployment state after reconciliation to prevent duplicate deployment.
485
+
486
+ WHY: After perform_startup_reconciliation() deploys agents to .claude/agents/,
487
+ we need to save a deployment state file so that ClaudeRunner.setup_agents()
488
+ can detect agents are already deployed and skip redundant deployment.
489
+
490
+ This prevents the "✓ Deployed 31 native agents" duplicate deployment that
491
+ occurs when setup_agents() doesn't know reconciliation already ran.
492
+
493
+ Args:
494
+ agent_result: DeploymentResult from perform_startup_reconciliation()
495
+ project_path: Project root directory
496
+
497
+ DESIGN DECISION: Use same state file format as ClaudeRunner._save_deployment_state()
498
+ Located at: .claude-mpm/cache/deployment_state.json
499
+
500
+ State file format:
501
+ {
502
+ "version": "5.6.13",
503
+ "agent_count": 15,
504
+ "deployment_hash": "sha256:...",
505
+ "deployed_at": 1234567890.123
506
+ }
507
+ """
508
+ import hashlib
509
+ import json
510
+ import time
511
+
512
+ from ..core.logger import get_logger
513
+
514
+ logger = get_logger("cli")
515
+
516
+ try:
517
+ # Get version from package
518
+ from claude_mpm import __version__
519
+
520
+ # Path to state file (matches ClaudeRunner._get_deployment_state_path())
521
+ state_file = project_path / ".claude-mpm" / "cache" / "deployment_state.json"
522
+ agents_dir = project_path / ".claude" / "agents"
523
+
524
+ # Count deployed agents
525
+ if agents_dir.exists():
526
+ agent_count = len(list(agents_dir.glob("*.md")))
527
+ else:
528
+ agent_count = 0
529
+
530
+ # Calculate deployment hash (matches ClaudeRunner._calculate_deployment_hash())
531
+ # CRITICAL: Must match exact hash algorithm used in ClaudeRunner
532
+ # Hashes filename + file content (not mtime) for consistency
533
+ deployment_hash = ""
534
+ if agents_dir.exists():
535
+ agent_files = sorted(agents_dir.glob("*.md"))
536
+ hash_obj = hashlib.sha256()
537
+ for agent_file in agent_files:
538
+ # Include filename and content in hash (matches ClaudeRunner)
539
+ hash_obj.update(agent_file.name.encode())
540
+ try:
541
+ hash_obj.update(agent_file.read_bytes())
542
+ except Exception as e:
543
+ logger.debug(f"Error reading {agent_file} for hash: {e}")
544
+
545
+ deployment_hash = hash_obj.hexdigest()
546
+
547
+ # Create state data
548
+ state_data = {
549
+ "version": __version__,
550
+ "agent_count": agent_count,
551
+ "deployment_hash": deployment_hash,
552
+ "deployed_at": time.time(),
553
+ }
554
+
555
+ # Ensure directory exists
556
+ state_file.parent.mkdir(parents=True, exist_ok=True)
557
+
558
+ # Write state file
559
+ state_file.write_text(json.dumps(state_data, indent=2))
560
+ logger.debug(
561
+ f"Saved deployment state after reconciliation: {agent_count} agents"
562
+ )
563
+
564
+ except Exception as e:
565
+ # Non-critical error - log but don't fail startup
566
+ logger.debug(f"Failed to save deployment state: {e}")
567
+
568
+
454
569
  def sync_remote_agents_on_startup(force_sync: bool = False):
455
570
  """
456
571
  Synchronize agent templates from remote sources on startup.
@@ -613,6 +728,12 @@ def sync_remote_agents_on_startup(force_sync: bool = False):
613
728
  )
614
729
  print(" Run with --verbose for detailed error information.\n")
615
730
 
731
+ # Save deployment state to prevent duplicate deployment in ClaudeRunner
732
+ # This ensures setup_agents() skips deployment since we already reconciled
733
+ _save_deployment_state_after_reconciliation(
734
+ agent_result=agent_result, project_path=project_path
735
+ )
736
+
616
737
  except Exception as e:
617
738
  # Deployment failure shouldn't block startup
618
739
  from ..core.logger import get_logger
@@ -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
 
claude_mpm/cli/utils.py CHANGED
@@ -107,7 +107,7 @@ def get_agent_versions_display() -> Optional[str]:
107
107
  base_version_tuple
108
108
  )
109
109
  output_lines.append(f"\n Base Agent Version: {base_version_str}")
110
- except Exception:
110
+ except Exception: # nosec B110 - intentional: version display is optional
111
111
  pass
112
112
 
113
113
  # Check for agents needing migration
@@ -173,8 +173,12 @@ def setup_logging(args) -> object:
173
173
  if not hasattr(args, "logging") or args.logging is None:
174
174
  args.logging = LogLevel.OFF.value
175
175
 
176
+ # Handle deprecated --verbose flag
177
+ if hasattr(args, "verbose") and args.verbose and args.logging == LogLevel.OFF.value:
178
+ args.logging = LogLevel.INFO.value
179
+
176
180
  # Handle deprecated --debug flag
177
- if hasattr(args, "debug") and args.debug and args.logging == LogLevel.INFO.value:
181
+ if hasattr(args, "debug") and args.debug:
178
182
  args.logging = LogLevel.DEBUG.value
179
183
 
180
184
  # Only setup logging if not OFF
@@ -204,7 +208,7 @@ def ensure_directories() -> None:
204
208
  from ..init import ensure_directories as init_ensure_directories
205
209
 
206
210
  init_ensure_directories()
207
- except Exception:
211
+ except Exception: # nosec B110
208
212
  # Continue even if initialization fails
209
213
  # The individual commands will handle missing directories as needed
210
214
  pass
@@ -0,0 +1,78 @@
1
+ """MPM Commander - Multi-Project Orchestration.
2
+
3
+ This module provides the core infrastructure for managing multiple projects
4
+ with isolated state, work queues, and tool sessions.
5
+
6
+ Key Components:
7
+ - ProjectRegistry: Thread-safe project management
8
+ - Project models: Data structures for state and sessions
9
+ - TmuxOrchestrator: Tmux session and pane management
10
+ - Config loading: .claude-mpm/ directory configuration
11
+ - CommanderDaemon: Main daemon process for orchestration
12
+ - ProjectSession: Per-project lifecycle management
13
+ - InstanceManager: Framework selection and instance lifecycle
14
+ - Frameworks: Claude Code, MPM framework abstractions
15
+ - Memory: Conversation storage, semantic search, context compression
16
+
17
+ Example:
18
+ >>> from claude_mpm.commander import ProjectRegistry
19
+ >>> registry = ProjectRegistry()
20
+ >>> project = registry.register("/path/to/project")
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)
27
+ """
28
+
29
+ from claude_mpm.commander.config import DaemonConfig
30
+ from claude_mpm.commander.config_loader import load_project_config
31
+ from claude_mpm.commander.daemon import CommanderDaemon
32
+ from claude_mpm.commander.frameworks import (
33
+ BaseFramework,
34
+ ClaudeCodeFramework,
35
+ InstanceInfo,
36
+ MPMFramework,
37
+ )
38
+ from claude_mpm.commander.instance_manager import (
39
+ FrameworkNotFoundError,
40
+ InstanceAlreadyExistsError,
41
+ InstanceManager,
42
+ InstanceNotFoundError,
43
+ )
44
+ from claude_mpm.commander.models import (
45
+ Project,
46
+ ProjectState,
47
+ ThreadMessage,
48
+ ToolSession,
49
+ )
50
+ from claude_mpm.commander.project_session import ProjectSession, SessionState
51
+ from claude_mpm.commander.registry import ProjectRegistry
52
+ from claude_mpm.commander.tmux_orchestrator import (
53
+ TmuxNotFoundError,
54
+ TmuxOrchestrator,
55
+ )
56
+
57
+ __all__ = [
58
+ "BaseFramework",
59
+ "ClaudeCodeFramework",
60
+ "CommanderDaemon",
61
+ "DaemonConfig",
62
+ "FrameworkNotFoundError",
63
+ "InstanceAlreadyExistsError",
64
+ "InstanceInfo",
65
+ "InstanceManager",
66
+ "InstanceNotFoundError",
67
+ "MPMFramework",
68
+ "Project",
69
+ "ProjectRegistry",
70
+ "ProjectSession",
71
+ "ProjectState",
72
+ "SessionState",
73
+ "ThreadMessage",
74
+ "TmuxNotFoundError",
75
+ "TmuxOrchestrator",
76
+ "ToolSession",
77
+ "load_project_config",
78
+ ]
@@ -0,0 +1,60 @@
1
+ """Runtime adapters for MPM Commander.
2
+
3
+ This package provides adapters for different AI coding tools, allowing
4
+ the TmuxOrchestrator to interface with various runtimes in a uniform way.
5
+
6
+ Two types of adapters:
7
+ - RuntimeAdapter: Synchronous parsing and state detection
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
18
+ """
19
+
20
+ from .auggie import AuggieAdapter
21
+ from .base import (
22
+ Capability,
23
+ ParsedResponse,
24
+ RuntimeAdapter,
25
+ RuntimeCapability,
26
+ RuntimeInfo,
27
+ )
28
+ from .claude_code import ClaudeCodeAdapter
29
+ from .codex import CodexAdapter
30
+ from .communication import (
31
+ AdapterResponse,
32
+ AdapterState,
33
+ BaseCommunicationAdapter,
34
+ ClaudeCodeCommunicationAdapter,
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)
44
+
45
+ __all__ = [
46
+ "AdapterRegistry",
47
+ "AdapterResponse",
48
+ "AdapterState",
49
+ "AuggieAdapter",
50
+ "BaseCommunicationAdapter",
51
+ "Capability",
52
+ "ClaudeCodeAdapter",
53
+ "ClaudeCodeCommunicationAdapter",
54
+ "CodexAdapter",
55
+ "MPMAdapter",
56
+ "ParsedResponse",
57
+ "RuntimeAdapter",
58
+ "RuntimeCapability",
59
+ "RuntimeInfo",
60
+ ]
@@ -0,0 +1,260 @@
1
+ """Auggie CLI runtime adapter.
2
+
3
+ This module implements the RuntimeAdapter interface for Auggie,
4
+ an AI coding assistant with MCP (Model Context Protocol) support.
5
+ """
6
+
7
+ import logging
8
+ import re
9
+ import shlex
10
+ from typing import List, Optional, Set
11
+
12
+ from .base import (
13
+ Capability,
14
+ ParsedResponse,
15
+ RuntimeAdapter,
16
+ RuntimeCapability,
17
+ RuntimeInfo,
18
+ )
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ class AuggieAdapter(RuntimeAdapter):
24
+ """Adapter for Auggie CLI.
25
+
26
+ Auggie is an AI coding assistant with support for MCP servers,
27
+ custom instructions, and various tool capabilities.
28
+
29
+ Example:
30
+ >>> adapter = AuggieAdapter()
31
+ >>> cmd = adapter.build_launch_command("/home/user/project")
32
+ >>> print(cmd)
33
+ cd '/home/user/project' && auggie
34
+ """
35
+
36
+ # Idle detection patterns
37
+ IDLE_PATTERNS = [
38
+ r"^>\s*$", # Simple prompt
39
+ r"auggie>\s*$", # Named prompt
40
+ r"Ready for input",
41
+ r"What would you like",
42
+ r"How can I assist",
43
+ ]
44
+
45
+ # Error patterns
46
+ ERROR_PATTERNS = [
47
+ r"Error:",
48
+ r"Failed:",
49
+ r"Exception:",
50
+ r"Permission denied",
51
+ r"not found",
52
+ r"Traceback \(most recent call last\)",
53
+ r"FATAL:",
54
+ r"command not found",
55
+ r"cannot access",
56
+ ]
57
+
58
+ # Question patterns
59
+ QUESTION_PATTERNS = [
60
+ r"Which option",
61
+ r"Should I proceed",
62
+ r"Please choose",
63
+ r"\(y/n\)\?",
64
+ r"Are you sure",
65
+ r"Do you want",
66
+ r"\[Y/n\]",
67
+ r"\[yes/no\]",
68
+ ]
69
+
70
+ # ANSI escape code pattern
71
+ ANSI_ESCAPE = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
72
+
73
+ @property
74
+ def name(self) -> str:
75
+ """Return the runtime identifier."""
76
+ return "auggie"
77
+
78
+ @property
79
+ def capabilities(self) -> Set[Capability]:
80
+ """Return the set of capabilities supported by Auggie."""
81
+ return {
82
+ Capability.TOOL_USE,
83
+ Capability.FILE_EDIT,
84
+ Capability.FILE_CREATE,
85
+ Capability.GIT_OPERATIONS,
86
+ Capability.SHELL_COMMANDS,
87
+ Capability.COMPLEX_REASONING,
88
+ }
89
+
90
+ @property
91
+ def runtime_info(self) -> RuntimeInfo:
92
+ """Return detailed runtime information."""
93
+ return RuntimeInfo(
94
+ name="auggie",
95
+ version=None, # Version detection could be added
96
+ capabilities={
97
+ RuntimeCapability.FILE_READ,
98
+ RuntimeCapability.FILE_EDIT,
99
+ RuntimeCapability.FILE_CREATE,
100
+ RuntimeCapability.BASH_EXECUTION,
101
+ RuntimeCapability.GIT_OPERATIONS,
102
+ RuntimeCapability.TOOL_USE,
103
+ RuntimeCapability.MCP_TOOLS, # Auggie supports MCP
104
+ RuntimeCapability.INSTRUCTIONS,
105
+ RuntimeCapability.COMPLEX_REASONING,
106
+ RuntimeCapability.AGENT_DELEGATION, # Auggie now supports agents
107
+ },
108
+ command="auggie",
109
+ supports_agents=True, # Auggie now supports agent delegation
110
+ instruction_file=".augment/instructions.md",
111
+ )
112
+
113
+ def build_launch_command(
114
+ self, project_path: str, agent_prompt: Optional[str] = None
115
+ ) -> str:
116
+ """Generate shell command to start Auggie.
117
+
118
+ Args:
119
+ project_path: Absolute path to the project directory
120
+ agent_prompt: Optional system prompt to configure Auggie
121
+
122
+ Returns:
123
+ Shell command string ready to execute
124
+
125
+ Example:
126
+ >>> adapter = AuggieAdapter()
127
+ >>> adapter.build_launch_command("/home/user/project")
128
+ "cd '/home/user/project' && auggie"
129
+ """
130
+ quoted_path = shlex.quote(project_path)
131
+ cmd = f"cd {quoted_path} && auggie"
132
+
133
+ if agent_prompt:
134
+ # Auggie may support --prompt or similar flag
135
+ # Adjust based on actual Auggie CLI options
136
+ quoted_prompt = shlex.quote(agent_prompt)
137
+ cmd += f" --prompt {quoted_prompt}"
138
+
139
+ logger.debug(f"Built Auggie launch command: {cmd}")
140
+ return cmd
141
+
142
+ def format_input(self, message: str) -> str:
143
+ """Prepare message for Auggie's input format."""
144
+ formatted = message.strip()
145
+ logger.debug(f"Formatted input: {formatted[:100]}...")
146
+ return formatted
147
+
148
+ def strip_ansi(self, text: str) -> str:
149
+ """Remove ANSI escape codes from text."""
150
+ return self.ANSI_ESCAPE.sub("", text)
151
+
152
+ def detect_idle(self, output: str) -> bool:
153
+ """Recognize when Auggie is waiting for input."""
154
+ if not output:
155
+ return False
156
+
157
+ clean = self.strip_ansi(output)
158
+ lines = clean.strip().split("\n")
159
+
160
+ if not lines:
161
+ return False
162
+
163
+ last_line = lines[-1].strip()
164
+
165
+ for pattern in self.IDLE_PATTERNS:
166
+ if re.search(pattern, last_line):
167
+ logger.debug(f"Detected idle state with pattern: {pattern}")
168
+ return True
169
+
170
+ return False
171
+
172
+ def detect_error(self, output: str) -> Optional[str]:
173
+ """Recognize error states and extract error message."""
174
+ clean = self.strip_ansi(output)
175
+
176
+ for pattern in self.ERROR_PATTERNS:
177
+ match = re.search(pattern, clean, re.IGNORECASE)
178
+ if match:
179
+ for line in clean.split("\n"):
180
+ if re.search(pattern, line, re.IGNORECASE):
181
+ error_msg = line.strip()
182
+ logger.warning(f"Detected error: {error_msg}")
183
+ return error_msg
184
+
185
+ return None
186
+
187
+ def detect_question(
188
+ self, output: str
189
+ ) -> tuple[bool, Optional[str], Optional[List[str]]]:
190
+ """Detect if Auggie is asking a question."""
191
+ clean = self.strip_ansi(output)
192
+
193
+ for pattern in self.QUESTION_PATTERNS:
194
+ if re.search(pattern, clean, re.IGNORECASE):
195
+ lines = clean.strip().split("\n")
196
+ question = None
197
+ options = []
198
+
199
+ for line in lines:
200
+ if re.search(pattern, line, re.IGNORECASE):
201
+ question = line.strip()
202
+
203
+ # Look for numbered options
204
+ opt_match = re.match(r"^\s*(\d+)[.):]\s*(.+)$", line)
205
+ if opt_match:
206
+ options.append(opt_match.group(2).strip())
207
+
208
+ logger.debug(
209
+ f"Detected question: {question}, options: {options if options else 'none'}"
210
+ )
211
+ return True, question, options if options else None
212
+
213
+ return False, None, None
214
+
215
+ def parse_response(self, output: str) -> ParsedResponse:
216
+ """Extract meaningful content from Auggie output."""
217
+ if not output:
218
+ return ParsedResponse(
219
+ content="",
220
+ is_complete=False,
221
+ is_error=False,
222
+ is_question=False,
223
+ )
224
+
225
+ clean = self.strip_ansi(output)
226
+ error_msg = self.detect_error(output)
227
+ is_question, question_text, options = self.detect_question(output)
228
+ is_complete = self.detect_idle(output)
229
+
230
+ response = ParsedResponse(
231
+ content=clean,
232
+ is_complete=is_complete,
233
+ is_error=error_msg is not None,
234
+ error_message=error_msg,
235
+ is_question=is_question,
236
+ question_text=question_text,
237
+ options=options,
238
+ )
239
+
240
+ logger.debug(
241
+ f"Parsed response: complete={is_complete}, error={error_msg is not None}, "
242
+ f"question={is_question}"
243
+ )
244
+
245
+ return response
246
+
247
+ def inject_instructions(self, instructions: str) -> Optional[str]:
248
+ """Return command to inject custom instructions.
249
+
250
+ Auggie supports .augment/instructions.md file for custom instructions.
251
+
252
+ Args:
253
+ instructions: Instructions text to inject
254
+
255
+ Returns:
256
+ Command to write instructions file
257
+ """
258
+ # Write to .augment/instructions.md
259
+ escaped = instructions.replace("'", "'\\''")
260
+ return f"mkdir -p .augment && echo '{escaped}' > .augment/instructions.md"