7c66aae0b8
Promote the project-rule constants (PROJECT_MARKERS, PROJECT_RULE_SUBDIRS, PROJECT_RULE_FILES, OPENCODE_USER_RULE_DIRS, USER_RULE_DIR, GITHUB_INSTRUCTIONS_PATTERN, RULE_EXTENSIONS, AGENTS_FILENAME, etc.) and the findAgentsMdUp walk-up helper out of the agents-md-core and rules-injector adapters and into @oh-my-opencode/rules-engine, the single owner of rule discovery. - packages/agents-md-core/ drops the findAgentsMdUp/AgentsMdDiscoveryInput wrappers (now sourced directly from rules-engine) and its constants module re-exports AGENTS_FILENAME from rules-engine instead of duplicating it. - src/hooks/directory-agents-injector/finder.ts pulls findAgentsMdUp from rules-engine directly while still re-exporting resolveFilePath from agents-md-core. - src/hooks/rules-injector/constants.ts becomes a pure re-export shim over the rules-engine constants. Add packages/agents-md-core/src/injector.test.ts to lock the root-skipping AGENTS.md injection order so future changes to findAgentsMdUp cannot silently regress the [Directory Context: ...] block format the injector emits. Tests: bun test packages/agents-md-core packages/rules-engine src/hooks/directory-agents-injector src/hooks/rules-injector
83 lines
2.6 KiB
TypeScript
83 lines
2.6 KiB
TypeScript
import { randomUUID } from "node:crypto";
|
|
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { afterEach, describe, expect, it } from "bun:test";
|
|
|
|
import { processFilePathForAgentsInjection } from "./injector";
|
|
|
|
describe("processFilePathForAgentsInjection", () => {
|
|
const sessionCaches = new Map<string, Set<string>>();
|
|
const storageBackfill = new Map<string, Set<string>>();
|
|
const storage = {
|
|
loadInjectedPaths: (sessionID: string): Set<string> =>
|
|
storageBackfill.get(sessionID) ?? new Set<string>(),
|
|
saveInjectedPaths: (sessionID: string, paths: Set<string>): void => {
|
|
storageBackfill.set(sessionID, paths);
|
|
},
|
|
};
|
|
|
|
const truncator = {
|
|
truncate: async (_sessionID: string, content: string) => ({
|
|
result: content,
|
|
truncated: false,
|
|
}),
|
|
};
|
|
|
|
let rootDirectory = "";
|
|
|
|
afterEach(() => {
|
|
if (rootDirectory) {
|
|
rmSync(rootDirectory, { recursive: true, force: true });
|
|
}
|
|
rootDirectory = "";
|
|
sessionCaches.clear();
|
|
storageBackfill.clear();
|
|
});
|
|
|
|
it("injects AGENTS.md chain in root-skipping order with unchanged context format", async () => {
|
|
// given
|
|
rootDirectory = join(tmpdir(), `agents-md-core-injector-${randomUUID()}`);
|
|
const srcDirectory = join(rootDirectory, "src");
|
|
const nestedDirectory = join(srcDirectory, "components");
|
|
mkdirSync(nestedDirectory, { recursive: true });
|
|
|
|
const rootAgents = "# root";
|
|
const srcAgents = "# src";
|
|
const nestedAgents = "# nested";
|
|
|
|
writeFileSync(join(rootDirectory, "AGENTS.md"), rootAgents);
|
|
writeFileSync(join(srcDirectory, "AGENTS.md"), srcAgents);
|
|
writeFileSync(join(nestedDirectory, "AGENTS.md"), nestedAgents);
|
|
writeFileSync(join(nestedDirectory, "button.ts"), "export const button = true;\n");
|
|
|
|
const output = {
|
|
title: "read result",
|
|
output: "base output",
|
|
metadata: {},
|
|
};
|
|
|
|
const srcAgentsPath = join(srcDirectory, "AGENTS.md");
|
|
const nestedAgentsPath = join(nestedDirectory, "AGENTS.md");
|
|
const expectedOutput =
|
|
"base output" +
|
|
`\n\n[Directory Context: ${srcAgentsPath}]\n${srcAgents}` +
|
|
`\n\n[Directory Context: ${nestedAgentsPath}]\n${nestedAgents}`;
|
|
|
|
// when
|
|
await processFilePathForAgentsInjection({
|
|
rootDirectory,
|
|
truncator,
|
|
sessionCaches,
|
|
storage,
|
|
filePath: join(nestedDirectory, "button.ts"),
|
|
sessionID: "session-regression",
|
|
output,
|
|
});
|
|
|
|
// then
|
|
expect(output.output).toBe(expectedOutput);
|
|
expect(output.output).not.toContain(rootAgents);
|
|
});
|
|
});
|