Implements adaptive rule injection similar to Claude Code's rule system: - Searches .cursor/rules and .claude/rules directories recursively - Supports YAML frontmatter with globs, paths, alwaysApply, description - Adaptive project root detection (finds markers even outside ctx.directory) - Symlink duplicate detection via realpath comparison - Content hash deduplication (SHA-256) to avoid re-injecting same rules - picomatch-based glob pattern matching for file-specific rules 🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
64 lines
1.6 KiB
TypeScript
64 lines
1.6 KiB
TypeScript
import { createHash } from "crypto"
|
|
import { relative } from "node:path"
|
|
import picomatch from "picomatch"
|
|
import type { RuleMetadata } from "./types"
|
|
|
|
export interface MatchResult {
|
|
applies: boolean
|
|
reason?: string
|
|
}
|
|
|
|
/**
|
|
* Check if a rule should apply to the current file based on metadata
|
|
*/
|
|
export function shouldApplyRule(
|
|
metadata: RuleMetadata,
|
|
currentFilePath: string,
|
|
projectRoot: string | null
|
|
): MatchResult {
|
|
if (metadata.alwaysApply === true) {
|
|
return { applies: true, reason: "alwaysApply" }
|
|
}
|
|
|
|
const globs = metadata.globs
|
|
if (!globs) {
|
|
return { applies: false }
|
|
}
|
|
|
|
const patterns = Array.isArray(globs) ? globs : [globs]
|
|
if (patterns.length === 0) {
|
|
return { applies: false }
|
|
}
|
|
|
|
const relativePath = projectRoot ? relative(projectRoot, currentFilePath) : currentFilePath
|
|
|
|
for (const pattern of patterns) {
|
|
if (picomatch.isMatch(relativePath, pattern, { dot: true, bash: true })) {
|
|
return { applies: true, reason: `glob: ${pattern}` }
|
|
}
|
|
}
|
|
|
|
return { applies: false }
|
|
}
|
|
|
|
/**
|
|
* Check if realPath already exists in cache (symlink deduplication)
|
|
*/
|
|
export function isDuplicateByRealPath(realPath: string, cache: Set<string>): boolean {
|
|
return cache.has(realPath)
|
|
}
|
|
|
|
/**
|
|
* Create SHA-256 hash of content, truncated to 16 chars
|
|
*/
|
|
export function createContentHash(content: string): string {
|
|
return createHash("sha256").update(content).digest("hex").slice(0, 16)
|
|
}
|
|
|
|
/**
|
|
* Check if content hash already exists in cache
|
|
*/
|
|
export function isDuplicateByContentHash(hash: string, cache: Set<string>): boolean {
|
|
return cache.has(hash)
|
|
}
|