feat(coding-agent): added automatic deduplication of context files by content
- Added automatic deduplication of identical context files by content, keeping the closest (lowest depth) copy when duplicates are discovered. - Implemented dedupeExactContextFiles() function to filter duplicate context entries in both explicit and discovered file lists. - Added 3 test cases covering deduplication of explicit context entries, discovered context entries, and preservation of distinct entries.
This commit is contained in:
@@ -5,7 +5,11 @@ import * as path from "node:path";
|
||||
import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import { createAgentSession } from "@oh-my-pi/pi-coding-agent/sdk";
|
||||
import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
|
||||
import { loadSystemPromptFiles } from "@oh-my-pi/pi-coding-agent/system-prompt";
|
||||
import {
|
||||
buildSystemPrompt,
|
||||
loadProjectContextFiles,
|
||||
loadSystemPromptFiles,
|
||||
} from "@oh-my-pi/pi-coding-agent/system-prompt";
|
||||
|
||||
function escapeRegExp(text: string): string {
|
||||
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
@@ -77,4 +81,62 @@ describe("SYSTEM.md prompt assembly", () => {
|
||||
|
||||
await expect(loadSystemPromptFiles({ cwd: projectDir })).resolves.toBe("Project SYSTEM prompt");
|
||||
});
|
||||
it("drops identical explicit context entries even when file names differ", async () => {
|
||||
const farPath = path.join(tempDir, "far", "AGENTS.md");
|
||||
const nearPath = path.join(tempDir, "near", "CLAUDE.md");
|
||||
const sharedContent = "Shared context instructions";
|
||||
|
||||
const prompt = await buildSystemPrompt({
|
||||
cwd: tempDir,
|
||||
customPrompt: "Base prompt",
|
||||
contextFiles: [
|
||||
{ path: farPath, content: sharedContent, depth: 2 },
|
||||
{ path: nearPath, content: sharedContent, depth: 0 },
|
||||
],
|
||||
skills: [],
|
||||
rules: [],
|
||||
toolNames: [],
|
||||
});
|
||||
|
||||
const matches = prompt.match(new RegExp(escapeRegExp(sharedContent), "g")) ?? [];
|
||||
expect(matches).toHaveLength(1);
|
||||
expect(prompt).not.toContain(`<file path="${farPath}">`);
|
||||
expect(prompt).toContain(`<file path="${nearPath}">`);
|
||||
});
|
||||
|
||||
it("drops identical discovered context entries and keeps the closest copy", async () => {
|
||||
const projectDir = path.join(tempDir, "project");
|
||||
const appDir = path.join(projectDir, "packages", "app");
|
||||
const sharedContent = "Shared context instructions";
|
||||
|
||||
fs.mkdirSync(appDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(projectDir, "AGENTS.md"), sharedContent);
|
||||
fs.writeFileSync(path.join(appDir, "AGENTS.md"), sharedContent);
|
||||
|
||||
const contextFiles = await loadProjectContextFiles({ cwd: appDir });
|
||||
const discoveredFiles = contextFiles.filter(file => file.path.startsWith(projectDir));
|
||||
|
||||
expect(discoveredFiles).toHaveLength(1);
|
||||
expect(discoveredFiles[0]?.path).toBe(path.join(appDir, "AGENTS.md"));
|
||||
});
|
||||
|
||||
it("keeps distinct context entries when their contents differ", async () => {
|
||||
const farPath = path.join(tempDir, "far", "AGENTS.md");
|
||||
const nearPath = path.join(tempDir, "near", "CLAUDE.md");
|
||||
|
||||
const prompt = await buildSystemPrompt({
|
||||
cwd: tempDir,
|
||||
customPrompt: "Base prompt",
|
||||
contextFiles: [
|
||||
{ path: farPath, content: "Root context instructions", depth: 2 },
|
||||
{ path: nearPath, content: "Near context instructions", depth: 0 },
|
||||
],
|
||||
skills: [],
|
||||
rules: [],
|
||||
toolNames: [],
|
||||
});
|
||||
|
||||
expect(prompt).toContain("Root context instructions");
|
||||
expect(prompt).toContain("Near context instructions");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user