From bcd7b8e34891ef0f2f7abd8506faa1d32f2669e0 Mon Sep 17 00:00:00 2001 From: Sisyphus Date: Sat, 18 Apr 2026 14:11:14 +0900 Subject: [PATCH] fix(rules-injector): memoize project-root lookup per process lifecycle Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/hooks/rules-injector/hook.ts | 3 +++ src/hooks/rules-injector/project-root-finder.ts | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/hooks/rules-injector/hook.ts b/src/hooks/rules-injector/hook.ts index f46af4570..2c37c9b4c 100644 --- a/src/hooks/rules-injector/hook.ts +++ b/src/hooks/rules-injector/hook.ts @@ -3,6 +3,7 @@ import { createDynamicTruncator } from "../../shared/dynamic-truncator"; import { getRuleInjectionFilePath } from "./output-path"; import { createSessionCacheStore } from "./cache"; import { createRuleInjectionProcessor } from "./injector"; +import { clearProjectRootCache } from "./project-root-finder"; interface ToolExecuteInput { tool: string; @@ -75,6 +76,7 @@ export function createRulesInjectorHook( if (sessionInfo?.id) { clearSessionCache(sessionInfo.id); } + clearProjectRootCache(); } if (event.type === "session.compacted") { @@ -83,6 +85,7 @@ export function createRulesInjectorHook( if (sessionID) { clearSessionCache(sessionID); } + clearProjectRootCache(); } }; diff --git a/src/hooks/rules-injector/project-root-finder.ts b/src/hooks/rules-injector/project-root-finder.ts index da697f0d9..ea552e0c9 100644 --- a/src/hooks/rules-injector/project-root-finder.ts +++ b/src/hooks/rules-injector/project-root-finder.ts @@ -2,6 +2,12 @@ import { existsSync, statSync } from "node:fs"; import { dirname, join } from "node:path"; import { PROJECT_MARKERS } from "./constants"; +const projectRootCache = new Map(); + +export function clearProjectRootCache(): void { + projectRootCache.clear(); +} + /** * Find project root by walking up from startPath. * Checks for PROJECT_MARKERS (.git, pyproject.toml, package.json, etc.) @@ -10,6 +16,16 @@ import { PROJECT_MARKERS } from "./constants"; * @returns Project root path or null if not found */ export function findProjectRoot(startPath: string): string | null { + if (projectRootCache.has(startPath)) { + return projectRootCache.get(startPath) ?? null; + } + + const projectRoot = findProjectRootWithoutCache(startPath); + projectRootCache.set(startPath, projectRoot); + return projectRoot; +} + +function findProjectRootWithoutCache(startPath: string): string | null { let current: string; try {