| 314 | } |
| 315 | |
| 316 | function normalizeRuleFileName(fileName: string): string { |
| 317 | const trimmed = fileName.trim() |
| 318 | |
| 319 | if (!trimmed) { |
| 320 | throw new Error("Rule name is required") |
| 321 | } |
| 322 | |
| 323 | if (path.isAbsolute(trimmed) || trimmed.includes("..") || trimmed.includes("/") || trimmed.includes("\\")) { |
| 324 | throw new Error("Rule name must be a file name, not a path") |
| 325 | } |
| 326 | |
| 327 | const ruleName = trimmed.endsWith(".md") ? trimmed.slice(0, -".md".length) : trimmed |
| 328 | if (ruleName.length > 64) { |
| 329 | throw new Error("Rule name must be 64 characters or less (excluding the .md suffix)") |
| 330 | } |
| 331 | |
| 332 | if (!VALID_RULE_FILENAME_PATTERN.test(trimmed)) { |
| 333 | throw new Error("Rule name must contain only lowercase letters, numbers, hyphens, and underscores") |
| 334 | } |
| 335 | |
| 336 | return trimmed.endsWith(".md") ? trimmed : `${trimmed}.md` |
| 337 | } |
| 338 | |
| 339 | function normalizeRelativeRulePath(relativePath: string): string { |
| 340 | const trimmed = relativePath.trim() |