oh-my-opencode/src/hooks/rules-injector/rule-file-scanner.ts
YeonGyu-Kim 2d22a54b55 refactor(rules-injector): split finder.ts into rule discovery modules
Extract rule finding logic:
- project-root-finder.ts: project root detection
- rule-file-finder.ts: rule file discovery
- rule-file-scanner.ts: filesystem scanning for rules
- rule-distance.ts: rule-to-file distance calculation
2026-02-08 16:22:33 +09:00

56 lines
1.5 KiB
TypeScript

import { existsSync, readdirSync, realpathSync } from "node:fs";
import { join } from "node:path";
import { GITHUB_INSTRUCTIONS_PATTERN, RULE_EXTENSIONS } from "./constants";
function isGitHubInstructionsDir(dir: string): boolean {
return dir.includes(".github/instructions") || dir.endsWith(".github/instructions");
}
function isValidRuleFile(fileName: string, dir: string): boolean {
if (isGitHubInstructionsDir(dir)) {
return GITHUB_INSTRUCTIONS_PATTERN.test(fileName);
}
return RULE_EXTENSIONS.some((ext) => fileName.endsWith(ext));
}
/**
* Recursively find all rule files (*.md, *.mdc) in a directory
*
* @param dir - Directory to search
* @param results - Array to accumulate results
*/
export function findRuleFilesRecursive(dir: string, results: string[]): void {
if (!existsSync(dir)) return;
try {
const entries = readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
findRuleFilesRecursive(fullPath, results);
} else if (entry.isFile()) {
if (isValidRuleFile(entry.name, dir)) {
results.push(fullPath);
}
}
}
} catch {
// Permission denied or other errors - silently skip
}
}
/**
* Resolve symlinks safely with fallback to original path
*
* @param filePath - Path to resolve
* @returns Real path or original path if resolution fails
*/
export function safeRealpathSync(filePath: string): string {
try {
return realpathSync(filePath);
} catch {
return filePath;
}
}