Fix context retrieval to collect ALL matching contexts

- getContextForPath now collects global + all matching path contexts
- getContextForFile also collects all contexts (global to specific)
- Contexts are sorted by specificity (most general to most specific)
- All contexts joined with double newline as requested
- Fixed store tests to use proper collection paths matching search terms
This commit is contained in:
Tobi Lutke 2025-12-13 13:12:30 -05:00
parent 4687394106
commit b52ba1ef61
No known key found for this signature in database
2 changed files with 92 additions and 17 deletions

View File

@ -191,7 +191,19 @@ async function insertTestDocument(
const now = new Date().toISOString();
const name = opts.name || "test-doc";
const title = opts.title || "Test Document";
const path = opts.displayPath || `test/${name}.md`;
// Use displayPath if provided, otherwise filepath's basename, otherwise default
let path: string;
if (opts.displayPath) {
path = opts.displayPath;
} else if (opts.filepath) {
// Extract relative path from filepath by removing collection path
// For tests, assume filepath is either relative or we want the whole path as the document path
path = opts.filepath.startsWith('/') ? opts.filepath : opts.filepath;
} else {
path = `test/${name}.md`;
}
const body = opts.body || "# Test Document\n\nThis is test content.";
const active = opts.active ?? 1;
@ -919,11 +931,10 @@ describe("Document Retrieval", () => {
test("findDocument includes context from path_contexts", async () => {
const store = await createTestStore();
const collectionName = await createTestCollection();
await addPathContext(collectionName, "/path/docs", "Documentation");
const collectionName = await createTestCollection({ pwd: "/path" });
await addPathContext(collectionName, "docs", "Documentation");
await insertTestDocument(store.db, collectionName, {
name: "mydoc",
filepath: "/path/docs/mydoc.md",
displayPath: "docs/mydoc.md",
});
@ -940,10 +951,10 @@ describe("Document Retrieval", () => {
describe("getDocumentBody", () => {
test("getDocumentBody returns full body", async () => {
const store = await createTestStore();
const collectionName = await createTestCollection();
const collectionName = await createTestCollection({ pwd: "/path" });
await insertTestDocument(store.db, collectionName, {
name: "mydoc",
filepath: "/path/mydoc.md",
displayPath: "mydoc.md",
body: "Line 1\nLine 2\nLine 3\nLine 4\nLine 5",
});
@ -955,10 +966,10 @@ describe("Document Retrieval", () => {
test("getDocumentBody supports line range", async () => {
const store = await createTestStore();
const collectionName = await createTestCollection();
const collectionName = await createTestCollection({ pwd: "/path" });
await insertTestDocument(store.db, collectionName, {
name: "mydoc",
filepath: "/path/mydoc.md",
displayPath: "mydoc.md",
body: "Line 1\nLine 2\nLine 3\nLine 4\nLine 5",
});
@ -1089,10 +1100,10 @@ describe("Document Retrieval", () => {
describe("Legacy getDocument", () => {
test("getDocument returns document with body", async () => {
const store = await createTestStore();
const collectionName = await createTestCollection();
const collectionName = await createTestCollection({ pwd: "/path" });
await insertTestDocument(store.db, collectionName, {
name: "mydoc",
filepath: "/path/mydoc.md",
displayPath: "mydoc.md",
body: "Document body",
});
@ -1107,10 +1118,9 @@ describe("Document Retrieval", () => {
test("getDocument supports line range from :line suffix", async () => {
const store = await createTestStore();
const collectionName = await createTestCollection();
const collectionName = await createTestCollection({ pwd: "/path" });
await insertTestDocument(store.db, collectionName, {
name: "mydoc",
filepath: "/path/mydoc.md",
displayPath: "mydoc.md",
body: "Line 1\nLine 2\nLine 3\nLine 4",
});

View File

@ -1020,8 +1020,43 @@ export function matchFilesByGlob(db: Database, pattern: string): { filepath: str
* @returns Context string or null if no context is defined
*/
export function getContextForPath(db: Database, collectionName: string, path: string): string | null {
const context = collectionsFindContextForPath(collectionName, path);
return context || null;
const config = collectionsLoadConfig();
const coll = getCollection(collectionName);
if (!coll) return null;
// Collect ALL matching contexts (global + all path prefixes)
const contexts: string[] = [];
// Add global context if present
if (config.global_context) {
contexts.push(config.global_context);
}
// Add all matching path contexts (from most general to most specific)
if (coll.context) {
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
// Collect all matching prefixes
const matchingContexts: { prefix: string; context: string }[] = [];
for (const [prefix, context] of Object.entries(coll.context)) {
const normalizedPrefix = prefix.startsWith("/") ? prefix : `/${prefix}`;
if (normalizedPath.startsWith(normalizedPrefix)) {
matchingContexts.push({ prefix: normalizedPrefix, context });
}
}
// Sort by prefix length (shortest/most general first)
matchingContexts.sort((a, b) => a.prefix.length - b.prefix.length);
// Add all matching contexts
for (const match of matchingContexts) {
contexts.push(match.context);
}
}
// Join all contexts with double newline
return contexts.length > 0 ? contexts.join('\n\n') : null;
}
/**
@ -1030,6 +1065,7 @@ export function getContextForPath(db: Database, collectionName: string, path: st
export function getContextForFile(db: Database, filepath: string): string | null {
// Get all collections from YAML config
const collections = collectionsListCollections();
const config = collectionsLoadConfig();
// Find which collection this absolute path belongs to
for (const coll of collections) {
@ -1048,9 +1084,38 @@ export function getContextForFile(db: Database, filepath: string): string | null
`).get(coll.name, relativePath) as { path: string } | null;
if (doc) {
// Use collections.ts to find context
const context = collectionsFindContextForPath(coll.name, relativePath);
return context || null;
// Collect ALL matching contexts (global + all path prefixes)
const contexts: string[] = [];
// Add global context if present
if (config.global_context) {
contexts.push(config.global_context);
}
// Add all matching path contexts (from most general to most specific)
if (coll.context) {
const normalizedPath = relativePath.startsWith("/") ? relativePath : `/${relativePath}`;
// Collect all matching prefixes
const matchingContexts: { prefix: string; context: string }[] = [];
for (const [prefix, context] of Object.entries(coll.context)) {
const normalizedPrefix = prefix.startsWith("/") ? prefix : `/${prefix}`;
if (normalizedPath.startsWith(normalizedPrefix)) {
matchingContexts.push({ prefix: normalizedPrefix, context });
}
}
// Sort by prefix length (shortest/most general first)
matchingContexts.sort((a, b) => a.prefix.length - b.prefix.length);
// Add all matching contexts
for (const match of matchingContexts) {
contexts.push(match.context);
}
}
// Join all contexts with double newline
return contexts.length > 0 ? contexts.join('\n\n') : null;
}
}
}