(candidateFiles)
| 257 | } |
| 258 | |
| 259 | function checkRuleFiles(candidateFiles) { |
| 260 | for (const file of candidateFiles) { |
| 261 | if (!isRuleFile(file)) continue; |
| 262 | |
| 263 | const fullPath = join(root, file); |
| 264 | if (!existsSync(fullPath) || !statSync(fullPath).isFile()) continue; |
| 265 | |
| 266 | const content = readFileSync(fullPath, "utf8"); |
| 267 | const trimmed = content.trim(); |
| 268 | |
| 269 | if (trimmed.length === 0) { |
| 270 | addFailure({ |
| 271 | ruleId: "rule-content/no-empty-file", |
| 272 | title: "Rule files must contain content", |
| 273 | file, |
| 274 | problem: `${file} is empty.`, |
| 275 | why: "Empty rule files pass path-level checks but provide no useful prompt content for Cursor users.", |
| 276 | fix: "Add complete rule content with valid frontmatter, or remove the empty file from the PR.", |
| 277 | }); |
| 278 | continue; |
| 279 | } |
| 280 | |
| 281 | if (looksLikeAiErrorMessage(trimmed)) { |
| 282 | addFailure({ |
| 283 | ruleId: "rule-content/no-ai-placeholder", |
| 284 | title: "Rule files must not be AI error placeholders", |
| 285 | file, |
| 286 | problem: `${file} looks like an AI error message, not rule content.`, |
| 287 | why: "AI apology or placeholder text usually means generation failed and the rule was committed without reviewable content.", |
| 288 | fix: "Replace the placeholder with real rule content, or remove the file until the rule is ready.", |
| 289 | }); |
| 290 | } |
| 291 | |
| 292 | if (isCanonicalMdcRule(file)) { |
| 293 | checkRuleFrontmatter(file, trimmed); |
| 294 | } |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | function checkPromptSafety(candidateFiles) { |
| 299 | for (const file of candidateFiles) { |
no test coverage detected