{"record":{"id":"8e05ba481d0f84f5","repo":"different-ai/openwork","slug":"skill-name-must-be-kebab-case","errorCode":null,"errorMessage":"skill name must be kebab-case","messagePattern":"skill name must be kebab-case","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/main.mjs","lineNumber":1273,"sourceCode":"    output += `description: ${escapeYamlScalar(command.description.trim())}\\n`;\n  }\n  if (typeof command?.agent === \"string\" && command.agent.trim()) {\n    output += `agent: ${escapeYamlScalar(command.agent.trim())}\\n`;\n  }\n  if (typeof command?.model === \"string\" && command.model.trim()) {\n    output += `model: ${escapeYamlScalar(command.model.trim())}\\n`;\n  }\n  if (command?.subtask === true) {\n    output += \"subtask: true\\n\";\n  }\n  output += `---\\n\\n${template}\\n`;\n  return output;\n}\n\nfunction validateSkillName(raw) {\n  const trimmed = String(raw ?? \"\").trim();\n  if (!trimmed || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(trimmed)) {\n    throw new Error(\"skill name must be kebab-case\");\n  }\n  return trimmed;\n}\n\nconst runtimeManager = createRuntimeManager({\n  app,\n  desktopRoot: path.resolve(__dirname, \"..\"),\n  listLocalWorkspacePaths: () => workspaceStore.listLocalWorkspacePaths(),\n  // When OPENWORK_ENCRYPTION_KEY is set, skip the safeStorage provider so it does not shadow the documented env override used by CI/headless/enterprise.\n  localManagedMcpVaultKey: process.env.OPENWORK_ENCRYPTION_KEY?.trim()\n    ? undefined\n    : createDesktopVaultKeyProvider({\n        filePath: path.join(app.getPath(\"userData\"), \"local-managed-mcp-vault-key.bin\"),\n        loadSafeStorage: () => require(\"electron\").safeStorage,\n      }),\n});\nconst initialRunnerBootstrap = workspaceStore.readDesktopBootstrapConfigSync();\nconst legacyRunnerBaseUrls = [","sourceCodeStart":1255,"sourceCodeEnd":1291,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/desktop/electron/main.mjs#L1255-L1291","documentation":"validateSkillName() enforces that a skill's directory/name is lowercase kebab-case: non-empty and matching /^[a-z0-9]+(?:-[a-z0-9]+)*$/. Names with uppercase, spaces, underscores, leading/trailing hyphens, or empty strings throw this error.","triggerScenarios":"Creating, importing, or syncing a skill whose name is \"My Skill\", \"my_skill\", \"MySkill\", \"-skill\", \"skill-\", or an empty/undefined value passed to validateSkillName().","commonSituations":"Users naming skills with spaces or CamelCase in the UI; importing skills from external sources that allow other conventions (e.g. snake_case); filesystem-derived names with non-ASCII characters; refactors leaving name undefined.","solutions":["Rename the skill to lowercase kebab-case, e.g. \"My Skill\" → \"my-skill\".","Replace underscores with hyphens and strip leading/trailing hyphens.","Slugify the name before validation: lowercase, replace non-alphanumerics with '-', collapse repeats.","Sanitize at the creation entry point so invalid names are rejected or transformed before persisting."],"exampleFix":"// before\nvalidateSkillName('My_Cool Skill'); // throws\n// after\nconst name = 'My_Cool Skill'\n  .toLowerCase()\n  .replace(/[^a-z0-9]+/g, '-')\n  .replace(/^-+|-+$/g, '');\nvalidateSkillName(name); // 'my-cool-skill'","handlingStrategy":"validation","validationCode":"const KEBAB = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\nif (!KEBAB.test(name.trim())) throw new TypeError('skill name must be kebab-case');","typeGuard":"function isKebabCase(v) {\n  return typeof v === 'string' && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(v);\n}","tryCatchPattern":"try {\n  validateSkillName(rawName);\n} catch (err) {\n  if (String(err.message) === 'skill name must be kebab-case') {\n    console.error('Invalid skill name:', rawName, '→ use lowercase-with-hyphens');\n  } else throw err;\n}","preventionTips":["Slugify names at creation: lowercase, strip non [a-z0-9-], collapse hyphens.","Show kebab-case hinting in any skill naming UI.","Reject names at input time, not at validation time.","Add unit tests for spaces, underscores, uppercase, and edge hyphens."],"tags":["validation","naming","skills"],"backgroundTag":"invalid-naming-convention","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}