(
userMessage: UserChatMessage | ToolResultChatMessage | undefined,
rules: RuleWithSource[],
contextItems: ContextItemWithId[],
rulePolicies: RulePolicies = {},
)
| 274 | * @returns List of applicable rules |
| 275 | */ |
| 276 | export const getApplicableRules = ( |
| 277 | userMessage: UserChatMessage | ToolResultChatMessage | undefined, |
| 278 | rules: RuleWithSource[], |
| 279 | contextItems: ContextItemWithId[], |
| 280 | rulePolicies: RulePolicies = {}, |
| 281 | ): RuleWithSource[] => { |
| 282 | // Get file paths from message and context for rule matching |
| 283 | const filePathsFromMessage = userMessage |
| 284 | ? extractPathsFromCodeBlocks(renderChatMessage(userMessage)) |
| 285 | : []; |
| 286 | |
| 287 | // Extract file paths from context items |
| 288 | const filePathsFromContextItems = contextItems |
| 289 | .filter((item) => item.uri?.type === "file" && item.uri?.value) |
| 290 | .map((item) => item.uri!.value); |
| 291 | |
| 292 | // Combine file paths from both sources |
| 293 | const allFilePaths = [...filePathsFromMessage, ...filePathsFromContextItems]; |
| 294 | |
| 295 | // Create a map of file paths to their contents for pattern matching |
| 296 | const fileContents: Record<string, string> = {}; |
| 297 | |
| 298 | // Extract contents from context items with file URIs |
| 299 | contextItems.forEach((item) => { |
| 300 | if (item.uri?.type === "file" && item.uri?.value) { |
| 301 | fileContents[item.uri.value] = item.content; |
| 302 | } |
| 303 | }); |
| 304 | |
| 305 | // Extract contents from code blocks in the message for paths that don't have content yet |
| 306 | if (userMessage) { |
| 307 | const messageContent = renderChatMessage(userMessage); |
| 308 | filePathsFromMessage.forEach((path) => { |
| 309 | // Only extract content if we don't already have it from context items |
| 310 | if (!fileContents[path]) { |
| 311 | const blockContent = extractContentFromCodeBlock(messageContent, path); |
| 312 | if (blockContent) { |
| 313 | fileContents[path] = blockContent; |
| 314 | } |
| 315 | } |
| 316 | }); |
| 317 | } |
| 318 | |
| 319 | // Apply shouldApplyRule to all rules - this will handle global rules, rule policies, |
| 320 | // and path matching in a consistent way |
| 321 | const applicableRules = rules.filter((rule) => |
| 322 | shouldApplyRule(rule, allFilePaths, rulePolicies, fileContents), |
| 323 | ); |
| 324 | |
| 325 | return applicableRules; |
| 326 | }; |
| 327 | |
| 328 | export function getRuleId(rule: RuleMetadata): string { |
| 329 | return rule.slug ?? rule.sourceFile ?? rule.name ?? rule.source; |
no test coverage detected