Manages preset lifecycle: installation, removal, updates.
| 542 | |
| 543 | |
| 544 | class PresetManager: |
| 545 | """Manages preset lifecycle: installation, removal, updates.""" |
| 546 | |
| 547 | def __init__(self, project_root: Path): |
| 548 | """Initialize preset manager. |
| 549 | |
| 550 | Args: |
| 551 | project_root: Path to project root directory |
| 552 | """ |
| 553 | self.project_root = project_root |
| 554 | self.presets_dir = project_root / ".specify" / "presets" |
| 555 | self.registry = PresetRegistry(self.presets_dir) |
| 556 | |
| 557 | def check_compatibility( |
| 558 | self, |
| 559 | manifest: PresetManifest, |
| 560 | speckit_version: str |
| 561 | ) -> bool: |
| 562 | """Check if preset is compatible with current spec-kit version. |
| 563 | |
| 564 | Args: |
| 565 | manifest: Preset manifest |
| 566 | speckit_version: Current spec-kit version |
| 567 | |
| 568 | Returns: |
| 569 | True if compatible |
| 570 | |
| 571 | Raises: |
| 572 | PresetCompatibilityError: If pack is incompatible |
| 573 | """ |
| 574 | required = manifest.requires_speckit_version |
| 575 | try: |
| 576 | SpecifierSet(required) # Just to validate |
| 577 | except InvalidSpecifier: |
| 578 | raise PresetCompatibilityError(f"Invalid version specifier: {required}") |
| 579 | |
| 580 | if not version_satisfies(speckit_version, required): |
| 581 | raise PresetCompatibilityError( |
| 582 | f"Preset requires spec-kit {required}, " |
| 583 | f"but {speckit_version} is installed.\n" |
| 584 | f"Upgrade spec-kit with: {REINSTALL_COMMAND}" |
| 585 | ) |
| 586 | |
| 587 | return True |
| 588 | |
| 589 | def _register_commands( |
| 590 | self, |
| 591 | manifest: PresetManifest, |
| 592 | preset_dir: Path |
| 593 | ) -> Dict[str, List[str]]: |
| 594 | """Register preset command overrides with all detected AI agents. |
| 595 | |
| 596 | Scans the preset's templates for type "command", reads each command |
| 597 | file, and writes it to every detected agent directory using the |
| 598 | CommandRegistrar from the agents module. |
| 599 | |
| 600 | When a command uses a composition strategy (prepend, append, wrap), |
| 601 | the content is composed with the lower-priority command before |
no outgoing calls