{"record":{"id":"43a6acb469e43585","repo":"charmbracelet/crush","slug":"name-must-be-alphanumeric-with-hyphens-no-leading","errorCode":null,"errorMessage":"name must be alphanumeric with hyphens, no leading/trailing/consecutive hyphens","messagePattern":"name must be alphanumeric with hyphens, no leading/trailing/consecutive hyphens","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/skills/skills.go","lineNumber":128,"sourceCode":"// arrives.\nfunc SetLatestStates(states []*SkillState) {\n\tlatestStatesMu.Lock()\n\tlatestStates = cloneStates(states)\n\tlatestStatesMu.Unlock()\n}\n\n// Validate checks if the skill meets spec requirements.\nfunc (s *Skill) Validate() error {\n\tvar errs []error\n\n\tif s.Name == \"\" {\n\t\terrs = append(errs, errors.New(\"name is required\"))\n\t} else {\n\t\tif len(s.Name) > MaxNameLength {\n\t\t\terrs = append(errs, fmt.Errorf(\"name exceeds %d characters\", MaxNameLength))\n\t\t}\n\t\tif !namePattern.MatchString(s.Name) {\n\t\t\terrs = append(errs, errors.New(\"name must be alphanumeric with hyphens, no leading/trailing/consecutive hyphens\"))\n\t\t}\n\t\tif s.Path != \"\" && !strings.EqualFold(filepath.Base(s.Path), s.Name) {\n\t\t\terrs = append(errs, fmt.Errorf(\"name %q must match directory %q\", s.Name, filepath.Base(s.Path)))\n\t\t}\n\t}\n\n\tif s.Description == \"\" {\n\t\terrs = append(errs, errors.New(\"description is required\"))\n\t} else if len(s.Description) > MaxDescriptionLength {\n\t\terrs = append(errs, fmt.Errorf(\"description exceeds %d characters\", MaxDescriptionLength))\n\t}\n\n\tif len(s.Compatibility) > MaxCompatibilityLength {\n\t\terrs = append(errs, fmt.Errorf(\"compatibility exceeds %d characters\", MaxCompatibilityLength))\n\t}\n\n\treturn errors.Join(errs...)\n}","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/skills/skills.go#L110-L146","documentation":"Skill.Validate rejects names that do not match namePattern: lowercase/direct alphanumeric with hyphens, and no leading, trailing, or consecutive hyphens. This keeps skill names valid as identifiers and directory names.","triggerScenarios":"A SKILL.md with `name: My Skill`, `name: -foo-`, `name: foo--bar`, or names with underscores/spaces is parsed and validated.","commonSituations":"Naming a skill with spaces or underscores out of habit; pasting a display title into the name field; renaming a directory without updating the frontmatter name.","solutions":["Rewrite the name using only alphanumerics and single interior hyphens (e.g. `code-review-helper`)","Remove leading/trailing hyphens and collapse consecutive hyphens","Make the name match the skill's directory name (see companion name/directory mismatch error)"],"exampleFix":"// before\nname: Code Review\n// after\nname: code-review","handlingStrategy":"validation","validationCode":"var nameRe = regexp.MustCompile(`^[a-z0-9]+(-[a-z0-9]+)*$`)\nif !nameRe.MatchString(skill.Name) {\n\treturn fmt.Errorf(\"invalid skill name %q\", skill.Name)\n}","typeGuard":null,"tryCatchPattern":"if err := skill.Validate(); err != nil {\n\treturn fmt.Errorf(\"invalid skill name: %w\", err)\n}","preventionTips":["Use kebab-case names matching the skill directory","Never use spaces, underscores, or edge hyphens in skill names","Keep name identical to filepath.Base(dir)"],"tags":["skills","validation","naming"],"backgroundTag":"schema-validation-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}