fix(hooks): improve rules injector implementation
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -43,33 +43,6 @@ interface ParsedRuleEntry {
|
||||
|
||||
const parsedRuleCache = new Map<string, ParsedRuleEntry>();
|
||||
|
||||
function getCachedParsedRule(
|
||||
filePath: string,
|
||||
realPath: string
|
||||
): { metadata: RuleMetadata; body: string } {
|
||||
try {
|
||||
const stat = statSync(filePath);
|
||||
const cached = parsedRuleCache.get(realPath);
|
||||
|
||||
if (cached && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {
|
||||
return { metadata: cached.metadata, body: cached.body };
|
||||
}
|
||||
|
||||
const rawContent = readFileSync(filePath, "utf-8");
|
||||
const { metadata, body } = parseRuleFrontmatter(rawContent);
|
||||
parsedRuleCache.set(realPath, {
|
||||
mtimeMs: stat.mtimeMs,
|
||||
size: stat.size,
|
||||
metadata,
|
||||
body,
|
||||
});
|
||||
return { metadata, body };
|
||||
} catch {
|
||||
const rawContent = readFileSync(filePath, "utf-8");
|
||||
return parseRuleFrontmatter(rawContent);
|
||||
}
|
||||
}
|
||||
|
||||
function resolveFilePath(
|
||||
workspaceDirectory: string,
|
||||
path: string
|
||||
@@ -84,6 +57,14 @@ export function createRuleInjectionProcessor(deps: {
|
||||
truncator: DynamicTruncator;
|
||||
getSessionCache: (sessionID: string) => SessionInjectedRulesCache;
|
||||
ruleFinderOptions?: FindRuleFilesOptions;
|
||||
readFileSync?: typeof readFileSync;
|
||||
statSync?: typeof statSync;
|
||||
homedir?: typeof homedir;
|
||||
shouldApplyRule?: typeof shouldApplyRule;
|
||||
isDuplicateByRealPath?: typeof isDuplicateByRealPath;
|
||||
createContentHash?: typeof createContentHash;
|
||||
isDuplicateByContentHash?: typeof isDuplicateByContentHash;
|
||||
saveInjectedRules?: typeof saveInjectedRules;
|
||||
}): {
|
||||
processFilePathForInjection: (
|
||||
filePath: string,
|
||||
@@ -91,7 +72,44 @@ export function createRuleInjectionProcessor(deps: {
|
||||
output: ToolExecuteOutput
|
||||
) => Promise<void>;
|
||||
} {
|
||||
const { workspaceDirectory, truncator, getSessionCache, ruleFinderOptions } = deps;
|
||||
const {
|
||||
workspaceDirectory,
|
||||
truncator,
|
||||
getSessionCache,
|
||||
ruleFinderOptions,
|
||||
readFileSync: readRuleFileSync = readFileSync,
|
||||
statSync: statRuleSync = statSync,
|
||||
homedir: getHomeDir = homedir,
|
||||
shouldApplyRule: shouldApplyRuleImpl = shouldApplyRule,
|
||||
isDuplicateByRealPath: isDuplicateByRealPathImpl = isDuplicateByRealPath,
|
||||
createContentHash: createContentHashImpl = createContentHash,
|
||||
isDuplicateByContentHash: isDuplicateByContentHashImpl = isDuplicateByContentHash,
|
||||
saveInjectedRules: saveInjectedRulesImpl = saveInjectedRules,
|
||||
} = deps;
|
||||
|
||||
function getParsedRule(filePath: string, realPath: string): { metadata: RuleMetadata; body: string } {
|
||||
try {
|
||||
const stat = statRuleSync(filePath);
|
||||
const cached = parsedRuleCache.get(realPath);
|
||||
|
||||
if (cached && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {
|
||||
return { metadata: cached.metadata, body: cached.body };
|
||||
}
|
||||
|
||||
const rawContent = readRuleFileSync(filePath, "utf-8");
|
||||
const { metadata, body } = parseRuleFrontmatter(rawContent);
|
||||
parsedRuleCache.set(realPath, {
|
||||
mtimeMs: stat.mtimeMs,
|
||||
size: stat.size,
|
||||
metadata,
|
||||
body,
|
||||
});
|
||||
return { metadata, body };
|
||||
} catch {
|
||||
const rawContent = readRuleFileSync(filePath, "utf-8");
|
||||
return parseRuleFrontmatter(rawContent);
|
||||
}
|
||||
}
|
||||
|
||||
async function processFilePathForInjection(
|
||||
filePath: string,
|
||||
@@ -103,17 +121,17 @@ export function createRuleInjectionProcessor(deps: {
|
||||
|
||||
const projectRoot = findProjectRoot(resolved);
|
||||
const cache = getSessionCache(sessionID);
|
||||
const home = homedir();
|
||||
const home = getHomeDir();
|
||||
|
||||
const ruleFileCandidates = findRuleFiles(projectRoot, home, resolved, ruleFinderOptions);
|
||||
const toInject: RuleToInject[] = [];
|
||||
let dirty = false;
|
||||
|
||||
for (const candidate of ruleFileCandidates) {
|
||||
if (isDuplicateByRealPath(candidate.realPath, cache.realPaths)) continue;
|
||||
if (isDuplicateByRealPathImpl(candidate.realPath, cache.realPaths)) continue;
|
||||
|
||||
try {
|
||||
const { metadata, body } = getCachedParsedRule(
|
||||
const { metadata, body } = getParsedRule(
|
||||
candidate.path,
|
||||
candidate.realPath
|
||||
);
|
||||
@@ -122,13 +140,13 @@ export function createRuleInjectionProcessor(deps: {
|
||||
if (candidate.isSingleFile) {
|
||||
matchReason = "copilot-instructions (always apply)";
|
||||
} else {
|
||||
const matchResult = shouldApplyRule(metadata, resolved, projectRoot);
|
||||
const matchResult = shouldApplyRuleImpl(metadata, resolved, projectRoot);
|
||||
if (!matchResult.applies) continue;
|
||||
matchReason = matchResult.reason ?? "matched";
|
||||
}
|
||||
|
||||
const contentHash = createContentHash(body);
|
||||
if (isDuplicateByContentHash(contentHash, cache.contentHashes)) continue;
|
||||
const contentHash = createContentHashImpl(body);
|
||||
if (isDuplicateByContentHashImpl(contentHash, cache.contentHashes)) continue;
|
||||
|
||||
const relativePath = projectRoot
|
||||
? relative(projectRoot, candidate.path)
|
||||
@@ -163,7 +181,7 @@ export function createRuleInjectionProcessor(deps: {
|
||||
}
|
||||
|
||||
if (dirty) {
|
||||
saveInjectedRules(sessionID, cache);
|
||||
saveInjectedRulesImpl(sessionID, cache);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user