{"record":{"id":"6b580788fb81719e","repo":"Kuberwastaken/claurst","slug":"plugin-name-cannot-contain-spaces-use-kebab","errorCode":null,"errorMessage":"Plugin name '{}' cannot contain spaces. Use kebab-case.","messagePattern":"Plugin name '(.+?)' cannot contain spaces\\. Use kebab-case\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-rust/crates/plugins/src/manifest.rs","lineNumber":347,"sourceCode":"        manifest.validate()?;\n        Ok(manifest)\n    }\n\n    /// Parse a manifest from TOML bytes (plugin.toml).\n    pub fn from_toml(bytes: &[u8]) -> anyhow::Result<Self> {\n        let s = std::str::from_utf8(bytes)?;\n        let manifest: PluginManifest = toml::from_str(s)?;\n        manifest.validate()?;\n        Ok(manifest)\n    }\n\n    /// Basic validation matching the TS schema checks.\n    pub fn validate(&self) -> anyhow::Result<()> {\n        if self.name.is_empty() {\n            anyhow::bail!(\"Plugin name cannot be empty\");\n        }\n        if self.name.contains(' ') {\n            anyhow::bail!(\n                \"Plugin name '{}' cannot contain spaces. Use kebab-case.\",\n                self.name\n            );\n        }\n        Ok(())\n    }\n}\n\n/// Normalise the raw JSON value so that both camelCase and snake_case\n/// variants of known fields work, and so `mcpServers` (object mapping) is\n/// converted to a `Vec<PluginMcpServer>`.\nfn normalize_manifest_json(mut v: serde_json::Value) -> serde_json::Value {\n    let obj = match v.as_object_mut() {\n        Some(o) => o,\n        None => return v,\n    };\n\n    // Promote `mcpServers` (TS camelCase object) → `mcp_servers` (array).","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/Kuberwastaken/claurst/blob/b0637c97ec34144387cbf2f74f65df6d16a6cef1/src-rust/crates/plugins/src/manifest.rs#L329-L365","documentation":"Plugin manifest names must be kebab-case; the validator rejects any name containing a space character. This keeps plugin names valid as identifiers on disk, in registries, and in CLI references, matching the TS schema checks.","triggerScenarios":"PluginManifest::validate (via from_toml) sees a name value containing at least one ' ' character, e.g. name = \"My Plugin\".","commonSituations":"Author wrote a display name instead of an identifier in plugin.toml; renaming a plugin to a human-friendly label; copied name from a README heading.","solutions":["Replace spaces in the name with hyphens, e.g. \"My Plugin\" -> \"my-plugin\"","Lowercase the name to conform to kebab-case conventions","Keep the human-readable title in a separate display-name field if the schema supports one"],"exampleFix":"// before (plugin.toml)\nname = \"My Plugin\"\n// after\nname = \"my-plugin\"","handlingStrategy":"validation","validationCode":"fn kebab_case(name: &str) -> bool {\n    !name.is_empty() && !name.contains(' ') && name.chars().all(|c| c.is_ascii_lowercase() || c == '-' || c.is_ascii_digit())\n}","typeGuard":null,"tryCatchPattern":"match PluginManifest::from_toml(&toml_str) {\n    Err(e) if e.to_string().contains(\"cannot contain spaces\") => { /* normalize name to kebab-case */ }\n    other => other?,\n}","preventionTips":["Always derive plugin names from kebab-case identifiers, not display names","Keep human-readable titles in a separate description/display field","Lint manifest names in CI for spaces and uppercase characters"],"tags":["plugins","manifest","validation","naming"],"backgroundTag":"invalid-identifier-format","analyzedSha":"b0637c97ec34144387cbf2f74f65df6d16a6cef1","analyzedAt":"2026-09-10T00:24:58.650Z","contentChangedAt":"2026-09-10T00:24:58.650Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}