MCPcopy
hub / github.com/github/spec-kit / _register_commands

Method _register_commands

src/specify_cli/presets/__init__.py:589–685  ·  view source on GitHub ↗

Register preset command overrides with all detected AI agents. Scans the preset's templates for type "command", reads each command file, and writes it to every detected agent directory using the CommandRegistrar from the agents module. When a command uses a composit

(
        self,
        manifest: PresetManifest,
        preset_dir: Path
    )

Source from the content-addressed store, hash-verified

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
602 registration.
603
604 Args:
605 manifest: Preset manifest
606 preset_dir: Installed preset directory
607
608 Returns:
609 Dictionary mapping agent names to lists of registered command names
610 """
611 command_templates = [
612 t for t in manifest.templates if t.get("type") == "command"
613 ]
614 if not command_templates:
615 return {}
616
617 # Filter out extension command overrides if the extension isn't installed.
618 # Command names follow the pattern: speckit.<ext-id>.<cmd-name>
619 # Core commands (e.g. speckit.specify) have only one dot — always register.
620 extensions_dir = self.project_root / ".specify" / "extensions"
621 filtered = []
622 for cmd in command_templates:
623 parts = cmd["name"].split(".")
624 if len(parts) >= 3 and parts[0] == "speckit":
625 ext_id = parts[1]
626 if not (extensions_dir / ext_id).is_dir():
627 continue
628 filtered.append(cmd)
629
630 if not filtered:
631 return {}
632
633 # Handle composition strategies: resolve composed content for non-replace commands
634 resolver = PresetResolver(self.project_root)
635 composed_dir = None
636 commands_to_register = []
637 for cmd in filtered:
638 strategy = cmd.get("strategy", "replace")
639 if strategy != "replace":
640 # Only pre-compose if this preset is the top composing layer.
641 # If a higher-priority replace already wins, skip composition
642 # here — reconciliation will write the correct content.
643 layers = resolver.collect_all_layers(cmd["name"], "command")
644 top_layer_is_ours = (
645 layers and layers[0]["path"].is_relative_to(preset_dir)
646 )

Callers 1

Calls 7

collect_all_layersMethod · 0.95
resolve_contentMethod · 0.95
PresetResolverClass · 0.85
CommandRegistrarClass · 0.50
getMethod · 0.45

Tested by

no test coverage detected