@@ -582,15 +582,151 @@ class ShtickCommands: |
| 582 | 582 | |
| 583 | 583 | def group_rename(self, old_name: str, new_name: str): |
| 584 | 584 | """Rename a group""" |
| 585 | | - # This would require refactoring the config to support renaming |
| 586 | | - # For now, just indicate it's not implemented |
| 587 | | - self._exit_error("Group rename is not yet implemented") |
| 585 | + try: |
| 586 | + # Check if old group exists |
| 587 | + if old_name not in self.manager.get_groups(): |
| 588 | + self._exit_error(f"Group '{old_name}' not found") |
| 589 | + |
| 590 | + # Check if new name already exists |
| 591 | + if new_name in self.manager.get_groups(): |
| 592 | + self._exit_error(f"Group '{new_name}' already exists") |
| 593 | + |
| 594 | + # Can't rename persistent group |
| 595 | + if old_name == "persistent": |
| 596 | + self._exit_error("Cannot rename the 'persistent' group") |
| 597 | + |
| 598 | + # Get the config |
| 599 | + from shtick.config import Config, GroupData |
| 600 | + |
| 601 | + config = self.manager._get_config() |
| 602 | + |
| 603 | + # Find the group to rename |
| 604 | + old_group = None |
| 605 | + for i, group in enumerate(config.groups): |
| 606 | + if group.name == old_name: |
| 607 | + old_group = group |
| 608 | + # Create new group with same content but new name |
| 609 | + new_group = GroupData( |
| 610 | + name=new_name, |
| 611 | + aliases=group.aliases.copy(), |
| 612 | + env_vars=group.env_vars.copy(), |
| 613 | + functions=group.functions.copy(), |
| 614 | + ) |
| 615 | + # Replace in the same position |
| 616 | + config.groups[i] = new_group |
| 617 | + break |
| 618 | + |
| 619 | + if not old_group: |
| 620 | + self._exit_error(f"Group '{old_name}' not found") |
| 621 | + |
| 622 | + # Update active groups if needed |
| 623 | + active_groups = config.load_active_groups() |
| 624 | + if old_name in active_groups: |
| 625 | + active_groups.remove(old_name) |
| 626 | + active_groups.append(new_name) |
| 627 | + config.save_active_groups(active_groups) |
| 628 | + |
| 629 | + # Save config |
| 630 | + config.save() |
| 631 | + |
| 632 | + # Rename the directory of generated files |
| 633 | + old_dir = os.path.join(config.get_output_dir(), old_name) |
| 634 | + new_dir = os.path.join(config.get_output_dir(), new_name) |
| 635 | + if os.path.exists(old_dir): |
| 636 | + os.rename(old_dir, new_dir) |
| 637 | + |
| 638 | + # Regenerate loader if group was active |
| 639 | + if new_name in active_groups: |
| 640 | + self.manager._generator.generate_loader(config) |
| 641 | + |
| 642 | + print(f"✓ Renamed group '{old_name}' to '{new_name}'") |
| 643 | + |
| 644 | + if new_name in active_groups: |
| 645 | + print(f"\nGroup '{new_name}' is still active") |
| 646 | + print("Run 'shtick generate' to update shell files") |
| 647 | + |
| 648 | + self._exit_success() |
| 649 | + except Exception as e: |
| 650 | + self._exit_error(f"Failed to rename group: {e}") |
| 588 | 651 | |
| 589 | 652 | def group_remove(self, name: str, force: bool = False): |
| 590 | 653 | """Remove a group""" |
| 591 | | - # This would require refactoring the config to support group removal |
| 592 | | - # For now, just indicate it's not implemented |
| 593 | | - self._exit_error("Group removal is not yet implemented") |
| 654 | + try: |
| 655 | + # Check if group exists |
| 656 | + if name not in self.manager.get_groups(): |
| 657 | + self._exit_error(f"Group '{name}' not found") |
| 658 | + |
| 659 | + # Can't remove persistent group |
| 660 | + if name == "persistent": |
| 661 | + self._exit_error("Cannot remove the 'persistent' group") |
| 662 | + |
| 663 | + # Get the config |
| 664 | + from shtick.config import Config |
| 665 | + |
| 666 | + config = self.manager._get_config() |
| 667 | + |
| 668 | + # Find the group |
| 669 | + group = config.get_group(name) |
| 670 | + if not group: |
| 671 | + self._exit_error(f"Group '{name}' not found") |
| 672 | + |
| 673 | + # Check if group has items and confirm if not forced |
| 674 | + if group.total_items > 0 and not force: |
| 675 | + print(f"Group '{name}' contains {group.total_items} items:") |
| 676 | + print(f" - {len(group.aliases)} aliases") |
| 677 | + print(f" - {len(group.env_vars)} environment variables") |
| 678 | + print(f" - {len(group.functions)} functions") |
| 679 | + |
| 680 | + try: |
| 681 | + response = ( |
| 682 | + input( |
| 683 | + f"\nAre you sure you want to remove group '{name}'? [y/N]: " |
| 684 | + ) |
| 685 | + .strip() |
| 686 | + .lower() |
| 687 | + ) |
| 688 | + if response not in ["y", "yes"]: |
| 689 | + print("Cancelled") |
| 690 | + self._exit_success() |
| 691 | + except (KeyboardInterrupt, EOFError): |
| 692 | + print("\nCancelled") |
| 693 | + self._exit_success() |
| 694 | + |
| 695 | + # Remove from groups list |
| 696 | + config.groups = [g for g in config.groups if g.name != name] |
| 697 | + |
| 698 | + # Remove from active groups if present |
| 699 | + active_groups = config.load_active_groups() |
| 700 | + was_active = name in active_groups |
| 701 | + if was_active: |
| 702 | + active_groups.remove(name) |
| 703 | + config.save_active_groups(active_groups) |
| 704 | + |
| 705 | + # Save config |
| 706 | + config.save() |
| 707 | + |
| 708 | + # Remove generated files directory |
| 709 | + group_dir = os.path.join(config.get_output_dir(), name) |
| 710 | + if os.path.exists(group_dir): |
| 711 | + import shutil |
| 712 | + |
| 713 | + shutil.rmtree(group_dir) |
| 714 | + |
| 715 | + # Regenerate loader if group was active |
| 716 | + if was_active: |
| 717 | + self.manager._generator.generate_loader(config) |
| 718 | + |
| 719 | + print(f"✓ Removed group '{name}'") |
| 720 | + |
| 721 | + if was_active: |
| 722 | + print( |
| 723 | + "\nGroup was active - changes will take effect in new shell sessions" |
| 724 | + ) |
| 725 | + self.offer_auto_source() |
| 726 | + |
| 727 | + self._exit_success() |
| 728 | + except Exception as e: |
| 729 | + self._exit_error(f"Failed to remove group: {e}") |
| 594 | 730 | |
| 595 | 731 | # Backup commands |
| 596 | 732 | def backup_create(self, name: str = None): |