fix(rules-core): fall back to workspace directory when no project root marker is found

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-05-20 13:04:20 +09:00
parent 2c62e7297b
commit 1f9a581e70
4 changed files with 27 additions and 6 deletions
+21 -5
View File
@@ -15,21 +15,37 @@ export function findRuleFiles(
): RuleFileCandidate[] {
const startDir = dirname(resolve(currentFile));
const skipClaudeUserRules = options?.skipClaudeUserRules ?? false;
const cacheKey = [projectRoot ?? "", startDir, skipClaudeUserRules ? "1" : "0"].join("\0");
const effectiveProjectRoot = resolveEffectiveProjectRoot(
projectRoot,
options?.workspaceDirectory,
startDir,
);
const cacheKey = [projectRoot ?? "", effectiveProjectRoot, startDir, skipClaudeUserRules ? "1" : "0"].join(
"\0",
);
const cached = cache?.get(cacheKey);
if (cached) return [...cached];
const candidates: RuleFileCandidate[] = [];
const seenRealPaths = new Set<string>();
if (projectRoot) {
addProjectRuleCandidates(projectRoot, startDir, candidates, seenRealPaths, cache);
addProjectSingleFileCandidates(projectRoot, candidates, seenRealPaths);
}
addProjectRuleCandidates(effectiveProjectRoot, startDir, candidates, seenRealPaths, cache);
addProjectSingleFileCandidates(effectiveProjectRoot, candidates, seenRealPaths);
addUserRuleCandidates(homeDir || homedir(), skipClaudeUserRules, candidates, seenRealPaths, cache);
const sorted = sortCandidates(candidates);
cache?.set(cacheKey, sorted);
return sorted;
}
function resolveEffectiveProjectRoot(
projectRoot: string | null,
workspaceDirectory: string | undefined,
startDir: string,
): string {
if (projectRoot) return projectRoot;
if (!workspaceDirectory) return startDir;
const workspaceRoot = resolve(workspaceDirectory);
return isSameOrChildPath(startDir, workspaceRoot) ? workspaceRoot : startDir;
}
function addProjectRuleCandidates(
projectRoot: string,
startDir: string,
+1
View File
@@ -58,6 +58,7 @@ export interface RuleScanCache {
export interface FindRuleFilesOptions {
readonly skipClaudeUserRules?: boolean;
readonly workspaceDirectory?: string;
}
export interface AgentsMdCache {
@@ -339,6 +339,7 @@ describe("createRuleInjectionProcessor", () => {
contentHashes: new Set<string>(),
realPaths: new Set<string>([ruleRealPath]),
}),
homedir: () => homeRoot,
});
// when
+4 -1
View File
@@ -137,6 +137,9 @@ export function createRuleInjectionProcessor(deps: {
} = deps;
const matchDecisionCache: MatchDecisionCache = new Map();
const finderOptions: FindRuleFilesOptions = ruleFinderOptions
? { ...ruleFinderOptions, workspaceDirectory }
: { workspaceDirectory };
function getParsedRule(filePath: string, realPath: string): ParsedRule {
try {
@@ -189,7 +192,7 @@ export function createRuleInjectionProcessor(deps: {
projectRoot,
home,
resolved,
ruleFinderOptions,
finderOptions,
ruleScanCache,
);
const toInject: RuleToInject[] = [];