diff --git a/src/cli/qmd.ts b/src/cli/qmd.ts index 22b5561c..be4757da 100755 --- a/src/cli/qmd.ts +++ b/src/cli/qmd.ts @@ -968,7 +968,7 @@ function multiGet(pattern: string, maxLines?: number, maxBytes: number = DEFAULT const db = getDb(); // Check if it's a comma-separated list or a glob pattern - const isCommaSeparated = pattern.includes(',') && !pattern.includes('*') && !pattern.includes('?'); + const isCommaSeparated = pattern.includes(',') && !pattern.includes('*') && !pattern.includes('?') && !pattern.includes('{'); let files: { filepath: string; displayPath: string; bodyLength: number; collection?: string; path?: string }[]; diff --git a/src/store.ts b/src/store.ts index f17404d8..faba9f4d 100644 --- a/src/store.ts +++ b/src/store.ts @@ -2245,7 +2245,7 @@ export function matchFilesByGlob(db: Database, pattern: string): { filepath: str const isMatch = picomatch(pattern); return allFiles - .filter(f => isMatch(f.virtual_path) || isMatch(f.path)) + .filter(f => isMatch(f.virtual_path) || isMatch(f.path) || isMatch(f.collection + '/' + f.path)) .map(f => ({ filepath: f.virtual_path, // Virtual path for precise lookup displayPath: f.path, // Relative path for display @@ -3354,7 +3354,7 @@ export function findDocuments( pattern: string, options: { includeBody?: boolean; maxBytes?: number } = {} ): { docs: MultiGetResult[]; errors: string[] } { - const isCommaSeparated = pattern.includes(',') && !pattern.includes('*') && !pattern.includes('?'); + const isCommaSeparated = pattern.includes(',') && !pattern.includes('*') && !pattern.includes('?') && !pattern.includes('{'); const errors: string[] = []; const maxBytes = options.maxBytes ?? DEFAULT_MULTI_GET_MAX_BYTES; diff --git a/test/store.test.ts b/test/store.test.ts index c5755f81..20065156 100644 --- a/test/store.test.ts +++ b/test/store.test.ts @@ -1757,6 +1757,55 @@ describe("Document Retrieval", () => { await cleanupTestDb(store); }); + + test("findDocuments supports brace expansion patterns", async () => { + const store = await createTestStore(); + const collectionName = await createTestCollection(); + + await insertTestDocument(store.db, collectionName, { + name: "doc1", + filepath: "/path/doc1.md", + displayPath: "doc1.md", + }); + await insertTestDocument(store.db, collectionName, { + name: "doc2", + filepath: "/path/doc2.md", + displayPath: "doc2.md", + }); + await insertTestDocument(store.db, collectionName, { + name: "doc3", + filepath: "/path/doc3.md", + displayPath: "doc3.md", + }); + + const { docs, errors } = store.findDocuments("{doc1,doc2}.md"); + expect(errors).toHaveLength(0); + expect(docs).toHaveLength(2); + + await cleanupTestDb(store); + }); + + test("findDocuments supports brace expansion with collection prefix", async () => { + const store = await createTestStore(); + const collectionName = await createTestCollection(); + + await insertTestDocument(store.db, collectionName, { + name: "readme", + filepath: "/path/readme.md", + displayPath: "readme.md", + }); + await insertTestDocument(store.db, collectionName, { + name: "changelog", + filepath: "/path/changelog.md", + displayPath: "changelog.md", + }); + + const { docs, errors } = store.findDocuments(`${collectionName}/{readme,changelog}.md`); + expect(errors).toHaveLength(0); + expect(docs).toHaveLength(2); + + await cleanupTestDb(store); + }); }); }); @@ -2115,6 +2164,48 @@ describe("Fuzzy Matching", () => { await cleanupTestDb(store); }); + + test("matchFilesByGlob matches collection/path patterns", async () => { + const store = await createTestStore(); + const collectionName = await createTestCollection(); + + await insertTestDocument(store.db, collectionName, { + filepath: "/p/readme.md", + displayPath: "readme.md", + }); + await insertTestDocument(store.db, collectionName, { + filepath: "/p/changelog.md", + displayPath: "changelog.md", + }); + + const matches = store.matchFilesByGlob(`${collectionName}/*.md`); + expect(matches).toHaveLength(2); + + await cleanupTestDb(store); + }); + + test("matchFilesByGlob matches brace expansion", async () => { + const store = await createTestStore(); + const collectionName = await createTestCollection(); + + await insertTestDocument(store.db, collectionName, { + filepath: "/p/readme.md", + displayPath: "readme.md", + }); + await insertTestDocument(store.db, collectionName, { + filepath: "/p/changelog.md", + displayPath: "changelog.md", + }); + await insertTestDocument(store.db, collectionName, { + filepath: "/p/license.md", + displayPath: "license.md", + }); + + const matches = store.matchFilesByGlob(`${collectionName}/{readme,changelog}.md`); + expect(matches).toHaveLength(2); + + await cleanupTestDb(store); + }); }); // =============================================================================