claude-mpm 4.7.7__py3-none-any.whl → 4.7.8__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/cli/commands/configure.py +245 -61
- {claude_mpm-4.7.7.dist-info → claude_mpm-4.7.8.dist-info}/METADATA +1 -1
- {claude_mpm-4.7.7.dist-info → claude_mpm-4.7.8.dist-info}/RECORD +8 -8
- {claude_mpm-4.7.7.dist-info → claude_mpm-4.7.8.dist-info}/WHEEL +0 -0
- {claude_mpm-4.7.7.dist-info → claude_mpm-4.7.8.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.7.7.dist-info → claude_mpm-4.7.8.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.7.7.dist-info → claude_mpm-4.7.8.dist-info}/top_level.txt +0 -0
claude_mpm/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.7.
|
1
|
+
4.7.8
|
@@ -60,6 +60,8 @@ class SimpleAgentManager:
|
|
60
60
|
import logging
|
61
61
|
|
62
62
|
self.logger = logging.getLogger(__name__)
|
63
|
+
# Track pending changes for batch operations
|
64
|
+
self.deferred_changes: Dict[str, bool] = {}
|
63
65
|
|
64
66
|
def _load_states(self):
|
65
67
|
"""Load agent states from file."""
|
@@ -85,6 +87,33 @@ class SimpleAgentManager:
|
|
85
87
|
self.states[agent_name]["enabled"] = enabled
|
86
88
|
self._save_states()
|
87
89
|
|
90
|
+
def set_agent_enabled_deferred(self, agent_name: str, enabled: bool) -> None:
|
91
|
+
"""Queue agent state change without saving."""
|
92
|
+
self.deferred_changes[agent_name] = enabled
|
93
|
+
|
94
|
+
def commit_deferred_changes(self) -> None:
|
95
|
+
"""Save all deferred changes at once."""
|
96
|
+
for agent_name, enabled in self.deferred_changes.items():
|
97
|
+
if agent_name not in self.states:
|
98
|
+
self.states[agent_name] = {}
|
99
|
+
self.states[agent_name]["enabled"] = enabled
|
100
|
+
self._save_states()
|
101
|
+
self.deferred_changes.clear()
|
102
|
+
|
103
|
+
def discard_deferred_changes(self) -> None:
|
104
|
+
"""Discard all pending changes."""
|
105
|
+
self.deferred_changes.clear()
|
106
|
+
|
107
|
+
def get_pending_state(self, agent_name: str) -> bool:
|
108
|
+
"""Get agent state including pending changes."""
|
109
|
+
if agent_name in self.deferred_changes:
|
110
|
+
return self.deferred_changes[agent_name]
|
111
|
+
return self.states.get(agent_name, {}).get("enabled", True)
|
112
|
+
|
113
|
+
def has_pending_changes(self) -> bool:
|
114
|
+
"""Check if there are unsaved changes."""
|
115
|
+
return len(self.deferred_changes) > 0
|
116
|
+
|
88
117
|
def discover_agents(self) -> List[AgentConfig]:
|
89
118
|
"""Discover available agents from template JSON files."""
|
90
119
|
agents = []
|
@@ -296,6 +325,33 @@ class ConfigureCommand(BaseCommand):
|
|
296
325
|
self._switch_scope()
|
297
326
|
elif choice == "6":
|
298
327
|
self._show_version_info_interactive()
|
328
|
+
elif choice == "l":
|
329
|
+
# Check for pending agent changes
|
330
|
+
if self.agent_manager and self.agent_manager.has_pending_changes():
|
331
|
+
should_save = Confirm.ask(
|
332
|
+
"[yellow]You have unsaved agent changes. Save them before launching?[/yellow]",
|
333
|
+
default=True,
|
334
|
+
)
|
335
|
+
if should_save:
|
336
|
+
self.agent_manager.commit_deferred_changes()
|
337
|
+
self.console.print("[green]✓ Agent changes saved[/green]")
|
338
|
+
else:
|
339
|
+
self.agent_manager.discard_deferred_changes()
|
340
|
+
self.console.print(
|
341
|
+
"[yellow]⚠ Agent changes discarded[/yellow]"
|
342
|
+
)
|
343
|
+
|
344
|
+
# Save all configuration
|
345
|
+
self.console.print("\n[cyan]Saving configuration...[/cyan]")
|
346
|
+
if self._save_all_configuration():
|
347
|
+
# Launch Claude MPM (this will replace the process if successful)
|
348
|
+
self._launch_claude_mpm()
|
349
|
+
# If execvp fails, we'll return here and break
|
350
|
+
break
|
351
|
+
self.console.print(
|
352
|
+
"[red]✗ Failed to save configuration. Not launching.[/red]"
|
353
|
+
)
|
354
|
+
Prompt.ask("\nPress Enter to continue")
|
299
355
|
elif choice == "q":
|
300
356
|
self.console.print(
|
301
357
|
"\n[green]Configuration complete. Goodbye![/green]"
|
@@ -356,16 +412,17 @@ class ConfigureCommand(BaseCommand):
|
|
356
412
|
),
|
357
413
|
("5", "Switch Scope", f"Current: {self.current_scope}"),
|
358
414
|
("6", "Version Info", "Display MPM and Claude versions"),
|
359
|
-
("
|
415
|
+
("l", "Save & Launch", "Save all changes and start Claude MPM"),
|
416
|
+
("q", "Quit", "Exit without launching"),
|
360
417
|
]
|
361
418
|
|
362
419
|
table = Table(show_header=False, box=None, padding=(0, 2))
|
363
|
-
table.add_column("Key", style="cyan", width=
|
364
|
-
table.add_column("Option", style="bold white", width=
|
365
|
-
table.add_column("Description", style="
|
420
|
+
table.add_column("Key", style="cyan bold", width=4) # Bolder shortcuts
|
421
|
+
table.add_column("Option", style="bold white", width=24) # Wider for titles
|
422
|
+
table.add_column("Description", style="white") # Better contrast
|
366
423
|
|
367
424
|
for key, option, desc in menu_items:
|
368
|
-
table.add_row(f"[{key}]", option, desc)
|
425
|
+
table.add_row(f"\\[{key}]", option, desc)
|
369
426
|
|
370
427
|
menu_panel = Panel(
|
371
428
|
table, title="[bold]Main Menu[/bold]", box=ROUNDED, style="green"
|
@@ -392,15 +449,10 @@ class ConfigureCommand(BaseCommand):
|
|
392
449
|
self.console.print("\n[bold]Agent Management Options:[/bold]")
|
393
450
|
|
394
451
|
# Use Text objects to properly display shortcuts with styling
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
self.console.print(
|
399
|
-
|
400
|
-
text_d = Text(" ")
|
401
|
-
text_d.append("[d]", style="cyan bold")
|
402
|
-
text_d.append(" Disable an agent")
|
403
|
-
self.console.print(text_d)
|
452
|
+
text_t = Text(" ")
|
453
|
+
text_t.append("[t]", style="cyan bold")
|
454
|
+
text_t.append(" Toggle agents (enable/disable multiple)")
|
455
|
+
self.console.print(text_t)
|
404
456
|
|
405
457
|
text_c = Text(" ")
|
406
458
|
text_c.append("[c]", style="cyan bold")
|
@@ -428,10 +480,8 @@ class ConfigureCommand(BaseCommand):
|
|
428
480
|
|
429
481
|
if choice == "b":
|
430
482
|
break
|
431
|
-
if choice == "
|
432
|
-
self.
|
433
|
-
elif choice == "d":
|
434
|
-
self._disable_agent_interactive(agents)
|
483
|
+
if choice == "t":
|
484
|
+
self._toggle_agents_interactive(agents)
|
435
485
|
elif choice == "c":
|
436
486
|
self._customize_agent_template(agents)
|
437
487
|
elif choice == "v":
|
@@ -494,55 +544,127 @@ class ConfigureCommand(BaseCommand):
|
|
494
544
|
|
495
545
|
self.console.print(table)
|
496
546
|
|
497
|
-
def
|
498
|
-
"""
|
499
|
-
|
547
|
+
def _display_agents_with_pending_states(self, agents: List[AgentConfig]) -> None:
|
548
|
+
"""Display agents table with pending state indicators."""
|
549
|
+
has_pending = self.agent_manager.has_pending_changes()
|
550
|
+
pending_count = len(self.agent_manager.deferred_changes) if has_pending else 0
|
500
551
|
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
self.agent_manager.set_agent_enabled(agent.name, True)
|
505
|
-
self.console.print("[green]All agents enabled successfully![/green]")
|
506
|
-
else:
|
507
|
-
try:
|
508
|
-
idx = int(agent_id) - 1
|
509
|
-
if 0 <= idx < len(agents):
|
510
|
-
agent = agents[idx]
|
511
|
-
self.agent_manager.set_agent_enabled(agent.name, True)
|
512
|
-
self.console.print(
|
513
|
-
f"[green]Agent '{agent.name}' enabled successfully![/green]"
|
514
|
-
)
|
515
|
-
else:
|
516
|
-
self.console.print("[red]Invalid agent ID.[/red]")
|
517
|
-
except ValueError:
|
518
|
-
self.console.print("[red]Invalid input. Please enter a number.[/red]")
|
552
|
+
title = f"Available Agents ({len(agents)} total)"
|
553
|
+
if has_pending:
|
554
|
+
title += f" [yellow]({pending_count} change{'s' if pending_count != 1 else ''} pending)[/yellow]"
|
519
555
|
|
520
|
-
|
556
|
+
table = Table(title=title, box=ROUNDED, show_lines=True, expand=True)
|
557
|
+
table.add_column("ID", justify="right", style="cyan", width=5)
|
558
|
+
table.add_column("Name", style="bold", width=22)
|
559
|
+
table.add_column("Status", width=20)
|
560
|
+
table.add_column("Description", style="bold cyan", width=45)
|
521
561
|
|
522
|
-
|
523
|
-
|
524
|
-
|
562
|
+
for idx, agent in enumerate(agents, 1):
|
563
|
+
current_state = self.agent_manager.is_agent_enabled(agent.name)
|
564
|
+
pending_state = self.agent_manager.get_pending_state(agent.name)
|
525
565
|
|
526
|
-
|
527
|
-
if
|
528
|
-
|
529
|
-
|
530
|
-
self.console.print("[green]All agents disabled successfully![/green]")
|
531
|
-
else:
|
532
|
-
try:
|
533
|
-
idx = int(agent_id) - 1
|
534
|
-
if 0 <= idx < len(agents):
|
535
|
-
agent = agents[idx]
|
536
|
-
self.agent_manager.set_agent_enabled(agent.name, False)
|
537
|
-
self.console.print(
|
538
|
-
f"[green]Agent '{agent.name}' disabled successfully![/green]"
|
539
|
-
)
|
566
|
+
# Show pending status with arrow
|
567
|
+
if current_state != pending_state:
|
568
|
+
if pending_state:
|
569
|
+
status = "[yellow]✗ Disabled → ✓ Enabled[/yellow]"
|
540
570
|
else:
|
541
|
-
|
542
|
-
|
543
|
-
|
571
|
+
status = "[yellow]✓ Enabled → ✗ Disabled[/yellow]"
|
572
|
+
else:
|
573
|
+
status = (
|
574
|
+
"[green]✓ Enabled[/green]"
|
575
|
+
if current_state
|
576
|
+
else "[dim]✗ Disabled[/dim]"
|
577
|
+
)
|
544
578
|
|
545
|
-
|
579
|
+
desc_display = Text()
|
580
|
+
desc_display.append(
|
581
|
+
(
|
582
|
+
agent.description[:42] + "..."
|
583
|
+
if len(agent.description) > 42
|
584
|
+
else agent.description
|
585
|
+
),
|
586
|
+
style="cyan",
|
587
|
+
)
|
588
|
+
|
589
|
+
table.add_row(str(idx), agent.name, status, desc_display)
|
590
|
+
|
591
|
+
self.console.print(table)
|
592
|
+
|
593
|
+
def _toggle_agents_interactive(self, agents: List[AgentConfig]) -> None:
|
594
|
+
"""Interactive multi-agent enable/disable with batch save."""
|
595
|
+
|
596
|
+
# Initialize pending states from current states
|
597
|
+
for agent in agents:
|
598
|
+
current_state = self.agent_manager.is_agent_enabled(agent.name)
|
599
|
+
self.agent_manager.set_agent_enabled_deferred(agent.name, current_state)
|
600
|
+
|
601
|
+
while True:
|
602
|
+
# Display table with pending states
|
603
|
+
self._display_agents_with_pending_states(agents)
|
604
|
+
|
605
|
+
# Show menu
|
606
|
+
self.console.print("\n[bold]Toggle Agent Status:[/bold]")
|
607
|
+
text_toggle = Text(" ")
|
608
|
+
text_toggle.append("[t]", style="cyan bold")
|
609
|
+
text_toggle.append(" Enter agent IDs to toggle (e.g., '1,3,5' or '1-4')")
|
610
|
+
self.console.print(text_toggle)
|
611
|
+
|
612
|
+
text_all = Text(" ")
|
613
|
+
text_all.append("[a]", style="cyan bold")
|
614
|
+
text_all.append(" Enable all agents")
|
615
|
+
self.console.print(text_all)
|
616
|
+
|
617
|
+
text_none = Text(" ")
|
618
|
+
text_none.append("[n]", style="cyan bold")
|
619
|
+
text_none.append(" Disable all agents")
|
620
|
+
self.console.print(text_none)
|
621
|
+
|
622
|
+
text_save = Text(" ")
|
623
|
+
text_save.append("[s]", style="green bold")
|
624
|
+
text_save.append(" Save changes and return")
|
625
|
+
self.console.print(text_save)
|
626
|
+
|
627
|
+
text_cancel = Text(" ")
|
628
|
+
text_cancel.append("[c]", style="yellow bold")
|
629
|
+
text_cancel.append(" Cancel (discard changes)")
|
630
|
+
self.console.print(text_cancel)
|
631
|
+
|
632
|
+
choice = (
|
633
|
+
Prompt.ask("[bold cyan]Select an option[/bold cyan]", default="s")
|
634
|
+
.strip()
|
635
|
+
.lower()
|
636
|
+
)
|
637
|
+
|
638
|
+
if choice == "s":
|
639
|
+
if self.agent_manager.has_pending_changes():
|
640
|
+
self.agent_manager.commit_deferred_changes()
|
641
|
+
self.console.print("[green]✓ Changes saved successfully![/green]")
|
642
|
+
else:
|
643
|
+
self.console.print("[yellow]No changes to save.[/yellow]")
|
644
|
+
Prompt.ask("Press Enter to continue")
|
645
|
+
break
|
646
|
+
if choice == "c":
|
647
|
+
self.agent_manager.discard_deferred_changes()
|
648
|
+
self.console.print("[yellow]Changes discarded.[/yellow]")
|
649
|
+
Prompt.ask("Press Enter to continue")
|
650
|
+
break
|
651
|
+
if choice == "a":
|
652
|
+
for agent in agents:
|
653
|
+
self.agent_manager.set_agent_enabled_deferred(agent.name, True)
|
654
|
+
elif choice == "n":
|
655
|
+
for agent in agents:
|
656
|
+
self.agent_manager.set_agent_enabled_deferred(agent.name, False)
|
657
|
+
elif choice == "t" or choice.replace(",", "").replace("-", "").isdigit():
|
658
|
+
selected_ids = self._parse_id_selection(
|
659
|
+
choice if choice != "t" else Prompt.ask("Enter IDs"), len(agents)
|
660
|
+
)
|
661
|
+
for idx in selected_ids:
|
662
|
+
if 1 <= idx <= len(agents):
|
663
|
+
agent = agents[idx - 1]
|
664
|
+
current = self.agent_manager.get_pending_state(agent.name)
|
665
|
+
self.agent_manager.set_agent_enabled_deferred(
|
666
|
+
agent.name, not current
|
667
|
+
)
|
546
668
|
|
547
669
|
def _customize_agent_template(self, agents: List[AgentConfig]) -> None:
|
548
670
|
"""Customize agent JSON template."""
|
@@ -1707,6 +1829,68 @@ class ConfigureCommand(BaseCommand):
|
|
1707
1829
|
Prompt.ask("Press Enter to continue")
|
1708
1830
|
return False
|
1709
1831
|
|
1832
|
+
def _save_all_configuration(self) -> bool:
|
1833
|
+
"""Save all configuration changes across all contexts.
|
1834
|
+
|
1835
|
+
Returns:
|
1836
|
+
bool: True if all saves successful, False otherwise
|
1837
|
+
"""
|
1838
|
+
try:
|
1839
|
+
# 1. Save any pending agent changes
|
1840
|
+
if self.agent_manager and self.agent_manager.has_pending_changes():
|
1841
|
+
self.agent_manager.commit_deferred_changes()
|
1842
|
+
self.console.print("[green]✓ Agent changes saved[/green]")
|
1843
|
+
|
1844
|
+
# 2. Save configuration file
|
1845
|
+
config = Config()
|
1846
|
+
|
1847
|
+
# Determine config file path based on scope
|
1848
|
+
if self.current_scope == "project":
|
1849
|
+
config_file = self.project_dir / ".claude-mpm" / "configuration.yaml"
|
1850
|
+
else:
|
1851
|
+
config_file = Path.home() / ".claude-mpm" / "configuration.yaml"
|
1852
|
+
|
1853
|
+
config_file.parent.mkdir(parents=True, exist_ok=True)
|
1854
|
+
|
1855
|
+
# Save with suppressed logging to avoid duplicate messages
|
1856
|
+
import logging
|
1857
|
+
|
1858
|
+
root_logger = logging.getLogger("claude_mpm")
|
1859
|
+
original_level = root_logger.level
|
1860
|
+
root_logger.setLevel(logging.WARNING)
|
1861
|
+
|
1862
|
+
try:
|
1863
|
+
config.save(config_file, format="yaml")
|
1864
|
+
finally:
|
1865
|
+
root_logger.setLevel(original_level)
|
1866
|
+
|
1867
|
+
self.console.print(f"[green]✓ Configuration saved to {config_file}[/green]")
|
1868
|
+
return True
|
1869
|
+
|
1870
|
+
except Exception as e:
|
1871
|
+
self.console.print(f"[red]✗ Error saving configuration: {e}[/red]")
|
1872
|
+
import traceback
|
1873
|
+
|
1874
|
+
traceback.print_exc()
|
1875
|
+
return False
|
1876
|
+
|
1877
|
+
def _launch_claude_mpm(self) -> None:
|
1878
|
+
"""Launch Claude MPM run command, replacing current process."""
|
1879
|
+
self.console.print("\n[bold cyan]═══ Launching Claude MPM ═══[/bold cyan]\n")
|
1880
|
+
|
1881
|
+
try:
|
1882
|
+
# Use execvp to replace the current process with claude-mpm run
|
1883
|
+
# This ensures a clean transition from configurator to Claude MPM
|
1884
|
+
os.execvp("claude-mpm", ["claude-mpm", "run"])
|
1885
|
+
except Exception as e:
|
1886
|
+
self.console.print(
|
1887
|
+
f"[yellow]⚠ Could not launch Claude MPM automatically: {e}[/yellow]"
|
1888
|
+
)
|
1889
|
+
self.console.print(
|
1890
|
+
"[cyan]→ Please run 'claude-mpm run' manually to start.[/cyan]"
|
1891
|
+
)
|
1892
|
+
Prompt.ask("\nPress Enter to exit")
|
1893
|
+
|
1710
1894
|
def _switch_scope(self) -> None:
|
1711
1895
|
"""Switch between project and user scope."""
|
1712
1896
|
self.current_scope = "user" if self.current_scope == "project" else "project"
|
@@ -1,5 +1,5 @@
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=9JfxhnDtr-8l3kCP2U5TVXSErptHoga8m7XA8zqgGOc,4
|
2
|
-
claude_mpm/VERSION,sha256=
|
2
|
+
claude_mpm/VERSION,sha256=XWuLX-eNJrJGfyBd1GUrzseop7w6skHL0X8KHS__eFE,6
|
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=cChN3myrAcF3jC-6DvHnBFTEnwlDk-TAsIXPvUZr_yw,5953
|
@@ -79,7 +79,7 @@ claude_mpm/cli/commands/analyze_code.py,sha256=yWZpG0aL4XlhcthtvbUqnFSlnvvseWO3V
|
|
79
79
|
claude_mpm/cli/commands/cleanup.py,sha256=TukZoPVfAFSF4ICfKCQUibczDE73EJP8nbEbfuT8GhE,19768
|
80
80
|
claude_mpm/cli/commands/cleanup_orphaned_agents.py,sha256=JR8crvgrz7Sa6d-SI-gKywok5S9rwc_DzDVk_h85sVs,4467
|
81
81
|
claude_mpm/cli/commands/config.py,sha256=Yfi8WO-10_MYz2QipFw-yEzVvHKNQ6iSQXeyW5J85Cg,18559
|
82
|
-
claude_mpm/cli/commands/configure.py,sha256=
|
82
|
+
claude_mpm/cli/commands/configure.py,sha256=rj8IEAyI-TgRAnTxtOJ8AL2xGhP33gOte8z8WzKhsG4,92942
|
83
83
|
claude_mpm/cli/commands/dashboard.py,sha256=4jPTmTl97DRNNJlYREWeE1iDdkct1uL-vv24MZn9fj4,11403
|
84
84
|
claude_mpm/cli/commands/debug.py,sha256=YCfJ3aYf6hOCvLW_grdfINdEqI4RXVS28VJ7tkZBFS8,47115
|
85
85
|
claude_mpm/cli/commands/doctor.py,sha256=nNKLZG3Qv_UsHNgrmetrWKgS7Pe2Jn5vq5aXyl60wKQ,7310
|
@@ -786,9 +786,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=D0izRT8anjiUb_JG72zlJR_JAw1cDkb7kalN
|
|
786
786
|
claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
|
787
787
|
claude_mpm/validation/agent_validator.py,sha256=GprtAvu80VyMXcKGsK_VhYiXWA6BjKHv7O6HKx0AB9w,20917
|
788
788
|
claude_mpm/validation/frontmatter_validator.py,sha256=YpJlYNNYcV8u6hIOi3_jaRsDnzhbcQpjCBE6eyBKaFY,7076
|
789
|
-
claude_mpm-4.7.
|
790
|
-
claude_mpm-4.7.
|
791
|
-
claude_mpm-4.7.
|
792
|
-
claude_mpm-4.7.
|
793
|
-
claude_mpm-4.7.
|
794
|
-
claude_mpm-4.7.
|
789
|
+
claude_mpm-4.7.8.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
|
790
|
+
claude_mpm-4.7.8.dist-info/METADATA,sha256=0zNQ_GqGZeqiE8cxEiJK7LQNZ6gfeuJyvsVrunOlrEE,17517
|
791
|
+
claude_mpm-4.7.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
792
|
+
claude_mpm-4.7.8.dist-info/entry_points.txt,sha256=Vlw3GNi-OtTpKSrez04iNrPmxNxYDpIWxmJCxiZ5Tx8,526
|
793
|
+
claude_mpm-4.7.8.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
794
|
+
claude_mpm-4.7.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|