{"record":{"id":"507aa443882a74e8","repo":"github/spec-kit","slug":"extension-must-provide-at-least-one-command-hook","errorCode":null,"errorMessage":"Extension must provide at least one command, hook, or event (or a declared template/script)","messagePattern":"Extension must provide at least one command, hook, or event \\(or a declared template/script\\)","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/extensions/__init__.py","lineNumber":402,"sourceCode":"            raise ValidationError(\"Invalid provides.commands: expected a list\")\n        if \"templates\" in provides and not isinstance(templates, list):\n            raise ValidationError(\"Invalid provides.templates: expected a list\")\n        if \"scripts\" in provides and not isinstance(scripts, list):\n            raise ValidationError(\"Invalid provides.scripts: expected a list\")\n        if \"hooks\" in self.data and not isinstance(hooks, dict):\n            raise ValidationError(\"Invalid hooks: expected a mapping\")\n        if \"events\" in self.data:\n            from ..events import validate_events\n            validate_events(self.data)\n\n        has_commands = bool(commands)\n        has_hooks = bool(hooks)\n        has_events = bool(events)\n        has_templates = bool(templates)\n        has_scripts = bool(scripts)\n\n        if not has_commands and not has_hooks and not has_events and not has_templates and not has_scripts:\n            raise ValidationError(\n                \"Extension must provide at least one command, hook, or event \"\n                \"(or a declared template/script)\"\n            )\n\n        self._validate_provided_artifacts(templates, section=\"templates\", singular=\"template\")\n        self._validate_provided_artifacts(scripts, section=\"scripts\", singular=\"script\")\n\n        # Validate hook values (if present).\n        # Each event is a single mapping or a list of mappings.\n        if hooks:\n            for hook_name, hook_config in hooks.items():\n                if isinstance(hook_config, list) and not hook_config:\n                    raise ValidationError(\n                        f\"Invalid hook '{hook_name}': list must contain at least one entry\"\n                    )\n                for entry in coerce_hook_entries(hook_config):\n                    if not isinstance(entry, dict):\n                        raise ValidationError(","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/extensions/__init__.py#L384-L420","documentation":"An extension manifest must declare at least one contribution: a non-empty provides.commands, hooks, events, provides.templates, or provides.scripts. An extension that provides nothing is considered malformed — there is nothing to install or dispatch.","triggerScenarios":"A manifest where commands/templates/scripts lists are all empty or absent, and hooks/events are missing: all five has_* flags are falsy, so the combined check fires.","commonSituations":"Skeleton manifests generated from a template with empty lists, or an author commenting out all contributions while developing.","solutions":["Declare at least one command, hook, event, template, or script in the manifest.","If you meant to ship a command, fill in `provides.commands` with a name/file entry pointing at an existing file.","If the extension is events-only, ensure the events key is present and non-empty (events feed has_events)."],"exampleFix":"# before\nprovides:\n  commands: []\n\n# after\nprovides:\n  commands:\n    - name: check\n      file: commands/check.md","handlingStrategy":"validation","validationCode":"def has_any_contribution(data: dict) -> bool:\n    provides = data.get(\"provides\", {})\n    return bool(\n        provides.get(\"commands\")\n        or provides.get(\"templates\")\n        or provides.get(\"scripts\")\n        or data.get(\"hooks\")\n        or data.get(\"events\")\n    )","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Declare at least one command, hook, event, template, or script before installing.","Don't ship skeleton manifests with all-empty contribution lists."],"tags":["extension-manifest","validation","required-fields"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}