{"record":{"id":"99a79fa3735afe4c","repo":"github/spec-kit","slug":"invalid-command-name-cmd-name-must-follow","errorCode":null,"errorMessage":"Invalid command name '{cmd['name']}': must follow pattern 'speckit.{extension}.{command}'","messagePattern":"Invalid command name '(.+?)': must follow pattern 'speckit\\.(.+?)\\.(.+?)'","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/extensions/__init__.py","lineNumber":485,"sourceCode":"            cmd_file = cmd[\"file\"]\n            reason = relative_extension_path_violation(cmd_file)\n            if reason:\n                label = repr(cmd_file) if isinstance(cmd_file, str) else f\"for command '{cmd.get('name')}'\"\n                raise ValidationError(f\"Invalid command 'file' {label}: {reason}\")\n\n            # Validate command name format\n            if not EXTENSION_COMMAND_NAME_PATTERN.match(cmd[\"name\"]):\n                corrected = self._try_correct_command_name(cmd[\"name\"], ext[\"id\"])\n                if corrected:\n                    self.warnings.append(\n                        f\"Command name '{cmd['name']}' does not follow the required pattern \"\n                        f\"'speckit.{{extension}}.{{command}}'. Registering as '{corrected}'. \"\n                        f\"The extension author should update the manifest to use this name.\"\n                    )\n                    rename_map[cmd[\"name\"]] = corrected\n                    cmd[\"name\"] = corrected\n                else:\n                    raise ValidationError(\n                        f\"Invalid command name '{cmd['name']}': \"\n                        \"must follow pattern 'speckit.{extension}.{command}'\"\n                    )\n\n            # Validate alias types; no pattern enforcement on aliases — they are\n            # intentionally free-form to preserve community extension compatibility\n            # (e.g. 'speckit.verify' short aliases used by existing extensions).\n            aliases = cmd.get(\"aliases\")\n            if aliases is None:\n                cmd[\"aliases\"] = []\n                aliases = []\n            if not isinstance(aliases, list):\n                raise ValidationError(\n                    f\"Aliases for command '{cmd['name']}' must be a list\"\n                )\n            for alias in aliases:\n                if not isinstance(alias, str):\n                    raise ValidationError(","sourceCodeStart":467,"sourceCodeEnd":503,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/extensions/__init__.py#L467-L503","documentation":"A command name did not match the required pattern ^speckit\\.([a-z0-9-]+)\\.([a-z0-9-]+)$ (EXTENSION_COMMAND_NAME_PATTERN, extensions/__init__.py:62) AND the loader could not auto-correct it via _try_correct_command_name(). The auto-corrector fixes two legacy forms — 'speckit.command' → 'speckit.{ext_id}.command' and '{ext_id}.command' → 'speckit.{ext_id}.command' — so only names outside those salvageable formats reach this hard error. This is a manifest-authoring contract: all extension commands live in the speckit.{extension}.{command} namespace.","triggerScenarios":"Declaring a command named \"build\", \"myext:build\", \"speckit.MyExt.Build\" (uppercase), \"speckit.myext.sub.build\" (three segments), or \"speckit.otherext.build\" (wrong extension id that correction cannot derive). The pattern check runs after the rename-map pass in ExtensionManifest._validate().","commonSituations":"Porting a community extension authored before the namespacing rule; using uppercase or underscore-separated command slugs; copy-pasting a command name from a different extension and forgetting to change the middle segment.","solutions":["Rename the command in the manifest to 'speckit.<your-extension-id>.<lowercase-slug>', e.g. \"speckit.myext.build\".","Keep every segment lowercase alphanumeric plus hyphens; use hyphens not underscores.","If you intended a short alias like 'speckit.verify', keep the canonical three-part name and declare the short form under 'aliases' instead (aliases are free-form)."],"exampleFix":"// before\n{ \"name\": \"build\", \"file\": \"commands/build.md\" }\n// after\n{ \"name\": \"speckit.myext.build\", \"file\": \"commands/build.md\", \"aliases\": [\"build\"] }","handlingStrategy":"validation","validationCode":"import re\nPATTERN = re.compile(r\"^speckit\\.([a-z0-9-]+)\\.([a-z0-9-]+)$\")\n\nbad = [c[\"name\"] for c in manifest[\"provides\"][\"commands\"]\n       if \"name\" in c and isinstance(c[\"name\"], str) and not PATTERN.match(c[\"name\"])]\nassert not bad, f\"non-conforming command names (not auto-correctable?): {bad}\"","typeGuard":"def is_canonical_command_name(name: str, ext_id: str) -> bool:\n    import re\n    return bool(re.match(r\"^speckit\\.[a-z0-9-]+\\.[a-z0-9-]+$\", name)) and name.split(\".\")[1] == ext_id","tryCatchPattern":"try:\n    ExtensionManifest.load(path)\nexcept ValidationError as e:\n    if \"must follow pattern 'speckit\" in str(e):\n        # rename to speckit.<ext-id>.<slug>; note typos may be auto-corrected with a warning instead\n        ...","preventionTips":["Name every command speckit.<extension-id>.<lowercase-slug> from the start.","Use hyphens, never underscores or uppercase, in command segments.","Put short forms in 'aliases', not the primary name."],"tags":["extensions","manifest","naming-convention","validation"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}