Remove an installed preset. Args: pack_id: Preset ID Returns: True if pack was removed
(self, pack_id: str)
| 1674 | return self.install_from_directory(pack_dir, speckit_version, priority) |
| 1675 | |
| 1676 | def remove(self, pack_id: str) -> bool: |
| 1677 | """Remove an installed preset. |
| 1678 | |
| 1679 | Args: |
| 1680 | pack_id: Preset ID |
| 1681 | |
| 1682 | Returns: |
| 1683 | True if pack was removed |
| 1684 | """ |
| 1685 | if not self.registry.is_installed(pack_id): |
| 1686 | return False |
| 1687 | |
| 1688 | metadata = self.registry.get(pack_id) |
| 1689 | # Restore original skills when preset is removed |
| 1690 | registered_skills = metadata.get("registered_skills", []) if metadata else [] |
| 1691 | registered_commands = metadata.get("registered_commands", {}) if metadata else {} |
| 1692 | pack_dir = self.presets_dir / pack_id |
| 1693 | |
| 1694 | # Collect ALL command names before filtering for reconciliation, |
| 1695 | # so commands registered only for skill-based agents are also reconciled. |
| 1696 | # Also include aliases from the manifest as a safety net for registries |
| 1697 | # populated by older versions that may not track aliases. |
| 1698 | removed_cmd_names = set() |
| 1699 | for cmd_names in registered_commands.values(): |
| 1700 | removed_cmd_names.update(cmd_names) |
| 1701 | manifest_path = pack_dir / "preset.yml" |
| 1702 | if manifest_path.exists(): |
| 1703 | try: |
| 1704 | manifest = PresetManifest(manifest_path) |
| 1705 | for tmpl in manifest.templates: |
| 1706 | if tmpl.get("type") == "command": |
| 1707 | for alias in tmpl.get("aliases", []): |
| 1708 | if isinstance(alias, str): |
| 1709 | removed_cmd_names.add(alias) |
| 1710 | except PresetValidationError: |
| 1711 | # Invalid manifest — skip alias extraction; primary command |
| 1712 | # names from registered_commands are still unregistered. |
| 1713 | pass |
| 1714 | |
| 1715 | if registered_skills: |
| 1716 | self._unregister_skills(registered_skills, pack_dir) |
| 1717 | try: |
| 1718 | from ..agents import CommandRegistrar |
| 1719 | except ImportError: |
| 1720 | CommandRegistrar = None |
| 1721 | if CommandRegistrar is not None: |
| 1722 | registered_commands = { |
| 1723 | agent_name: cmd_names |
| 1724 | for agent_name, cmd_names in registered_commands.items() |
| 1725 | if CommandRegistrar.AGENT_CONFIGS.get(agent_name, {}).get("extension") != "/SKILL.md" |
| 1726 | } |
| 1727 | |
| 1728 | # Unregister non-skill command files from AI agents. |
| 1729 | if registered_commands: |
| 1730 | self._unregister_commands(registered_commands) |
| 1731 | |
| 1732 | if pack_dir.exists(): |
| 1733 | shutil.rmtree(pack_dir) |