{"_id":"@tobilu/qmd","_rev":"16-e8f9bad7e10a781479a3a9117a347dd9","name":"@tobilu/qmd","dist-tags":{"latest":"2.8.3"},"versions":{"0.9.0":{"name":"@tobilu/qmd","version":"0.9.0","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"license":"MIT","_id":"@tobilu/qmd@0.9.0","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"qmd"},"dist":{"shasum":"8af09dccf3485e70a7f3c5525ecea11ecebabac1","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-0.9.0.tgz","fileCount":11,"integrity":"sha512-OVtzUJ+9omxQNNFuLbfxsaTmpsZlziRaj1a7iwGk89vkmokUXQVS9QJY356JiBiypkiV532cH2lS9POevada/Q==","signatures":[{"sig":"MEUCIQC36qSXAFuasGu88fSpJg+muFbuX5ciiV50AF2IZW3KxQIgCoeAk9/NNyoT/+OpjLAvm4GmZD9pL6hL0/nhgDmzhxM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":308692},"type":"module","engines":{"bun":">=1.0.0"},"gitHead":"5d73752b47040780231c1c149106dc45352fb2c6","scripts":{"qmd":"bun src/qmd.ts","link":"bun link","test":"bun test --preload ./src/test-preload.ts","index":"bun src/qmd.ts index","rerank":"bun src/qmd.ts rerank","search":"bun src/qmd.ts search","vector":"bun src/qmd.ts vector","release":"./scripts/release.sh","vsearch":"bun src/qmd.ts vsearch","inspector":"npx @modelcontextprotocol/inspector bun src/qmd.ts mcp"},"_npmUser":{"name":"tobilu","email":"tobi@lutke.com"},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"10.9.2","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"23.5.0","dependencies":{"zod":"^4.2.1","yaml":"^2.8.2","sqlite-vec":"^0.1.7-alpha.2","node-llama-cpp":"^3.14.5","@modelcontextprotocol/sdk":"^1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"latest"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"sqlite-vec-linux-x64":"^0.1.7-alpha.2","sqlite-vec-win32-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-arm64":"^0.1.7-alpha.2"},"_npmOperationalInternal":{"tmp":"tmp/qmd_0.9.0_1771182820865_0.8069312914478104","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@tobilu/qmd","version":"1.0.0","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","_id":"@tobilu/qmd@1.0.0","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"qmd"},"dist":{"shasum":"849590d962654b0e12af8111ea493cbe43a55636","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-1.0.0.tgz","fileCount":13,"integrity":"sha512-aQn1bjNyo2NN5MSFp/DzGM1PdlDrfRqCcmDriUhbvofpHyNQ67qvZpArX0Xt4ZoYXb9ur7BRehdaTYXGmIs9tg==","signatures":[{"sig":"MEUCIFPvOojfEhjlxB7WRSn6ByMGhE1vrPSarKV0+X3eRxQaAiEAgtRSn8h4yCHpDTCt2XgaJJihU3r3bJL0Ih71kRf6od4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":335790},"type":"module","engines":{"node":">=22.0.0"},"gitHead":"77c6eba1599b97f5ccfcc04b40925205f37ef2d4","scripts":{"qmd":"tsx src/qmd.ts","test":"vitest run --reporter=verbose test/","index":"tsx src/qmd.ts index","rerank":"tsx src/qmd.ts rerank","search":"tsx src/qmd.ts search","vector":"tsx src/qmd.ts vector","release":"./scripts/release.sh","vsearch":"tsx src/qmd.ts vsearch","inspector":"npx @modelcontextprotocol/inspector tsx src/qmd.ts mcp"},"_npmUser":{"name":"tobilu","email":"tobi@lutke.com"},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"10.9.4","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"22.22.0","dependencies":{"zod":"^4.2.1","yaml":"^2.8.2","fast-glob":"^3.3.0","picomatch":"^4.0.0","sqlite-vec":"^0.1.7-alpha.2","better-sqlite3":"^11.0.0","node-llama-cpp":"^3.14.5","@modelcontextprotocol/sdk":"^1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","vitest":"^3.0.0","@types/better-sqlite3":"^7.6.0"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"sqlite-vec-linux-x64":"^0.1.7-alpha.2","sqlite-vec-win32-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-arm64":"^0.1.7-alpha.2"},"_npmOperationalInternal":{"tmp":"tmp/qmd_1.0.0_1771211321520_0.028796969306884135","host":"s3://npm-registry-packages-npm-production"}},"1.0.5":{"name":"@tobilu/qmd","version":"1.0.5","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","_id":"@tobilu/qmd@1.0.5","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"qmd"},"dist":{"shasum":"1f8b2b395263cc4fc4f0c287f3412add7c1ff824","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-1.0.5.tgz","fileCount":19,"integrity":"sha512-er0tuqtrc9VZVomvZXy5CevNBzhCc+aAtKW+MsfMvNOvvpFsnO6KYvQoKU4slJOmP2Rh/bCqSEC2j3hzwBz94Q==","signatures":[{"sig":"MEYCIQCDBqcAzGlnlbi1xXnW9isrwuAGG169XrJYz/RWyrw0OwIhAJFGY5ADdAfsY8g9OwfamP7/l3FUsNqmAKJDAZxrmSch","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@1.0.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":369861},"type":"module","engines":{"node":">=22.0.0"},"gitHead":"8dd6cdcebf1f3f1472a280716c1299a00dca01c9","scripts":{"qmd":"tsx src/qmd.ts","test":"vitest run --reporter=verbose test/","build":"tsc -p tsconfig.build.json","index":"tsx src/qmd.ts index","rerank":"tsx src/qmd.ts rerank","search":"tsx src/qmd.ts search","vector":"tsx src/qmd.ts vector","prepare":"[ -d .git ] && ./scripts/install-hooks.sh || true","release":"./scripts/release.sh","vsearch":"tsx src/qmd.ts vsearch","inspector":"npx @modelcontextprotocol/inspector tsx src/qmd.ts mcp"},"_npmUser":{"name":"tobilu","email":"tobi@lutke.com"},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"10.9.4","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"22.22.0","dependencies":{"zod":"^4.2.1","yaml":"^2.8.2","fast-glob":"^3.3.0","picomatch":"^4.0.0","sqlite-vec":"^0.1.7-alpha.2","better-sqlite3":"^11.0.0","node-llama-cpp":"^3.14.5","@modelcontextprotocol/sdk":"^1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","vitest":"^3.0.0","@types/better-sqlite3":"^7.6.0"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"sqlite-vec-linux-x64":"^0.1.7-alpha.2","sqlite-vec-win32-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-arm64":"^0.1.7-alpha.2"},"_npmOperationalInternal":{"tmp":"tmp/qmd_1.0.5_1771246739414_0.9857006562402184","host":"s3://npm-registry-packages-npm-production"}},"1.0.6":{"name":"@tobilu/qmd","version":"1.0.6","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","_id":"@tobilu/qmd@1.0.6","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"qmd"},"dist":{"shasum":"0b359d0369efcb83c593cd2d256f1a4f20f4be61","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-1.0.6.tgz","fileCount":19,"integrity":"sha512-qjySMLUcDN8D8Jv22d7VKTa07Q4nXxA0sVDP7LE7kCywu8T7g+t9YQJS/7hwqLCsOZSGBAMPIdvWKihJIvRMsw==","signatures":[{"sig":"MEUCIQDeDpiZsa6q8/35c/3yn86p0zplt8T1pOKYeS2PmJb2vAIgfhokQhoudPn3C/ioi0t6xLLjhEjG2NKHjPaWg9yjTTc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@1.0.6","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":370606},"type":"module","engines":{"node":">=22.0.0"},"gitHead":"51c03d944506323e49c084c100c4e1ef1757f6e0","scripts":{"qmd":"tsx src/qmd.ts","test":"vitest run --reporter=verbose test/","build":"tsc -p tsconfig.build.json","index":"tsx src/qmd.ts index","rerank":"tsx src/qmd.ts rerank","search":"tsx src/qmd.ts search","vector":"tsx src/qmd.ts vector","prepare":"[ -d .git ] && ./scripts/install-hooks.sh || true","release":"./scripts/release.sh","vsearch":"tsx src/qmd.ts vsearch","inspector":"npx @modelcontextprotocol/inspector tsx src/qmd.ts mcp"},"_npmUser":{"name":"tobilu","email":"tobi@lutke.com"},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"10.9.4","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"22.22.0","dependencies":{"zod":"^4.2.1","yaml":"^2.8.2","fast-glob":"^3.3.0","picomatch":"^4.0.0","sqlite-vec":"^0.1.7-alpha.2","better-sqlite3":"^11.0.0","node-llama-cpp":"^3.14.5","@modelcontextprotocol/sdk":"^1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","vitest":"^3.0.0","@types/better-sqlite3":"^7.6.0"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"sqlite-vec-linux-x64":"^0.1.7-alpha.2","sqlite-vec-win32-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-arm64":"^0.1.7-alpha.2"},"_npmOperationalInternal":{"tmp":"tmp/qmd_1.0.6_1771252775207_0.910308075078446","host":"s3://npm-registry-packages-npm-production"}},"1.0.7":{"name":"@tobilu/qmd","version":"1.0.7","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","_id":"@tobilu/qmd@1.0.7","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"qmd"},"dist":{"shasum":"2bc2f4b20c0ba3cd36288c57536a55ef0b7c5f17","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-1.0.7.tgz","fileCount":19,"integrity":"sha512-80PnqmowNe3bfMy3EXJMY6JYdD2Ak1iQCXppbgO8x28Wufrsz65yc/5lBrigW5ThEtPRYIdwSEQTFTIY+v1pKA==","signatures":[{"sig":"MEUCIAC5+1ypLqo2B2MIOR8t09exOGnoTnrIOUTW6/fu44sIAiEA5+C0idYvLPF6z2sPGxK5hmk9V0MQXC+Ht6dA4fc4Phw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@1.0.7","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":374910},"type":"module","engines":{"node":">=22.0.0"},"gitHead":"648779a04db0120835cd911d917c7456f57c16b9","scripts":{"qmd":"tsx src/qmd.ts","test":"vitest run --reporter=verbose test/","build":"tsc -p tsconfig.build.json","index":"tsx src/qmd.ts index","rerank":"tsx src/qmd.ts rerank","search":"tsx src/qmd.ts search","vector":"tsx src/qmd.ts vector","prepare":"[ -d .git ] && ./scripts/install-hooks.sh || true","release":"./scripts/release.sh","vsearch":"tsx src/qmd.ts vsearch","inspector":"npx @modelcontextprotocol/inspector tsx src/qmd.ts mcp"},"_npmUser":{"name":"tobilu","email":"tobi@lutke.com"},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"10.9.4","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"22.22.0","dependencies":{"zod":"^4.2.1","yaml":"^2.8.2","fast-glob":"^3.3.0","picomatch":"^4.0.0","sqlite-vec":"^0.1.7-alpha.2","better-sqlite3":"^11.0.0","node-llama-cpp":"^3.14.5","@modelcontextprotocol/sdk":"^1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","vitest":"^3.0.0","@types/better-sqlite3":"^7.6.0"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"sqlite-vec-linux-x64":"^0.1.7-alpha.2","sqlite-vec-win32-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-arm64":"^0.1.7-alpha.2"},"_npmOperationalInternal":{"tmp":"tmp/qmd_1.0.7_1771444614286_0.7123000899469605","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@tobilu/qmd","version":"1.1.1","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","_id":"@tobilu/qmd@1.1.1","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"dist/qmd.js"},"dist":{"shasum":"388127163266842f0628b3a29c78e0e1f10722bf","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-1.1.1.tgz","fileCount":18,"integrity":"sha512-WXZ7TruyZSjYVJbrU4ka00LHSMsRMxDTuiQOTeQ2QX93mqQEXeXUVmHVEzE8Q5lZ8N/wtjKpLSdobqUiQbw8Sw==","signatures":[{"sig":"MEQCIBk+lnOsmBtYXCrzC/5HCmBo+hYBIGI31Rady9/DDl0uAiATn/rlmqujMeuKI91Kh8GXGlrgBBaz86XJfXz+fKNVdQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@1.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":408232},"type":"module","engines":{"node":">=22.0.0"},"gitHead":"2ae1baba2f08b1719424990356110becd2a6e100","scripts":{"qmd":"tsx src/qmd.ts","test":"vitest run --reporter=verbose test/","build":"tsc -p tsconfig.build.json && printf '#!/usr/bin/env node\n' | cat - dist/qmd.js > dist/qmd.tmp && mv dist/qmd.tmp dist/qmd.js && chmod +x dist/qmd.js","index":"tsx src/qmd.ts index","rerank":"tsx src/qmd.ts rerank","search":"tsx src/qmd.ts search","vector":"tsx src/qmd.ts vector","prepare":"[ -d .git ] && ./scripts/install-hooks.sh || true","release":"./scripts/release.sh","vsearch":"tsx src/qmd.ts vsearch","inspector":"npx @modelcontextprotocol/inspector tsx src/qmd.ts mcp"},"_npmUser":{"name":"tobilu","email":"tobi@lutke.com"},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"10.9.4","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"22.22.0","dependencies":{"zod":"^4.2.1","yaml":"^2.8.2","fast-glob":"^3.3.0","picomatch":"^4.0.0","sqlite-vec":"^0.1.7-alpha.2","better-sqlite3":"^11.0.0","node-llama-cpp":"^3.14.5","@modelcontextprotocol/sdk":"^1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","vitest":"^3.0.0","@types/better-sqlite3":"^7.6.0"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"sqlite-vec-linux-x64":"^0.1.7-alpha.2","sqlite-vec-win32-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-arm64":"^0.1.7-alpha.2"},"_npmOperationalInternal":{"tmp":"tmp/qmd_1.1.1_1772907172052_0.5130142468471095","host":"s3://npm-registry-packages-npm-production"}},"1.1.2":{"name":"@tobilu/qmd","version":"1.1.2","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","_id":"@tobilu/qmd@1.1.2","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"dist/qmd.js"},"dist":{"shasum":"15a5e282028d15916cb61ed58e6c095cdb8c781d","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-1.1.2.tgz","fileCount":18,"integrity":"sha512-jp3l7cN3P/TTFak0dQxE5XRkbHQpJaivSNjRD4VupoUEQtqBBay9t8SNne1892ImLhXmiLqVckF4XTC4WnGXsg==","signatures":[{"sig":"MEUCIQDqhynQycm9ugSrpecPxKwvXjfrDQUJjeVR+6wApYCiwwIgFrD85mUdyPefQRMiBuBQ6Z3/+eEoQIn93rt/LvXCQBo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@1.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":428623},"type":"module","engines":{"node":">=22.0.0"},"gitHead":"b838f74c8c29d99f994ca607810b39ef9fedc9f7","scripts":{"qmd":"tsx src/qmd.ts","test":"vitest run --reporter=verbose test/","build":"tsc -p tsconfig.build.json && printf '#!/usr/bin/env node\n' | cat - dist/qmd.js > dist/qmd.tmp && mv dist/qmd.tmp dist/qmd.js && chmod +x dist/qmd.js","index":"tsx src/qmd.ts index","rerank":"tsx src/qmd.ts rerank","search":"tsx src/qmd.ts search","vector":"tsx src/qmd.ts vector","prepare":"[ -d .git ] && ./scripts/install-hooks.sh || true","release":"./scripts/release.sh","vsearch":"tsx src/qmd.ts vsearch","inspector":"npx @modelcontextprotocol/inspector tsx src/qmd.ts mcp"},"_npmUser":{"name":"tobilu","email":"tobi@lutke.com"},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"10.9.4","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"22.22.0","dependencies":{"zod":"^4.2.1","yaml":"^2.8.2","fast-glob":"^3.3.0","picomatch":"^4.0.0","sqlite-vec":"^0.1.7-alpha.2","better-sqlite3":"^11.0.0","node-llama-cpp":"^3.17.1","@modelcontextprotocol/sdk":"^1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","vitest":"^3.0.0","@types/better-sqlite3":"^7.6.0"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"sqlite-vec-linux-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-x64":"^0.1.7-alpha.2","sqlite-vec-linux-arm64":"^0.1.7-alpha.2","sqlite-vec-windows-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-arm64":"^0.1.7-alpha.2"},"_npmOperationalInternal":{"tmp":"tmp/qmd_1.1.2_1772913716924_0.3510635830057174","host":"s3://npm-registry-packages-npm-production"}},"1.1.5":{"name":"@tobilu/qmd","version":"1.1.5","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","_id":"@tobilu/qmd@1.1.5","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"dist/qmd.js"},"dist":{"shasum":"fd56730d07675afed6d0d02d6bbc83d6f64ba88d","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-1.1.5.tgz","fileCount":18,"integrity":"sha512-Bea4NSf0WCZJV3xQMCo9merGn8G/3+L9EPilRQRWLWTXK0JPgPHmhbD9Ae63pdRn7YCHTM+08W+Ad/1odN3wwQ==","signatures":[{"sig":"MEUCIQDSoRm5VmBUVY5caEVlpxN1GK1mpZJ7bZOEigWdAKrakQIgO5styeK2QzboY3p7vOEPr1i7k66mUb/koIKnmyUWctY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@1.1.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":435948},"type":"module","engines":{"node":">=22.0.0"},"gitHead":"4fa11682db67160f6c11935ecc0435224f2441f9","scripts":{"qmd":"tsx src/qmd.ts","test":"vitest run --reporter=verbose test/","build":"tsc -p tsconfig.build.json && printf '#!/usr/bin/env node\n' | cat - dist/qmd.js > dist/qmd.tmp && mv dist/qmd.tmp dist/qmd.js && chmod +x dist/qmd.js","index":"tsx src/qmd.ts index","rerank":"tsx src/qmd.ts rerank","search":"tsx src/qmd.ts search","vector":"tsx src/qmd.ts vector","prepare":"[ -d .git ] && ./scripts/install-hooks.sh || true","release":"./scripts/release.sh","vsearch":"tsx src/qmd.ts vsearch","inspector":"npx @modelcontextprotocol/inspector tsx src/qmd.ts mcp"},"_npmUser":{"name":"tobilu","email":"tobi@lutke.com"},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"10.9.4","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"22.22.0","dependencies":{"zod":"^4.2.1","yaml":"^2.8.2","fast-glob":"^3.3.0","picomatch":"^4.0.0","sqlite-vec":"^0.1.7-alpha.2","better-sqlite3":"^11.0.0","node-llama-cpp":"^3.17.1","@modelcontextprotocol/sdk":"^1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","vitest":"^3.0.0","@types/better-sqlite3":"^7.6.0"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"sqlite-vec-linux-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-x64":"^0.1.7-alpha.2","sqlite-vec-linux-arm64":"^0.1.7-alpha.2","sqlite-vec-windows-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-arm64":"^0.1.7-alpha.2"},"_npmOperationalInternal":{"tmp":"tmp/qmd_1.1.5_1772933861779_0.3292684465371176","host":"s3://npm-registry-packages-npm-production"}},"1.1.6":{"name":"@tobilu/qmd","version":"1.1.6","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","_id":"@tobilu/qmd@1.1.6","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"dist/qmd.js"},"dist":{"shasum":"0bd21e095eee6e8460698b91f0c24d4f3d05e66d","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-1.1.6.tgz","fileCount":20,"integrity":"sha512-euhQuXxr2Fgdmuhbsxtrwcodkwhnsfzk6alsoitt2QX2BZHs/+2GgkTKW7X3akE2SK7BGtcIuMZeAa95CEBlnA==","signatures":[{"sig":"MEUCIQDwl1Gut1nvHgy1oXj6vckXs57A1DC7O7YvOH0jkvMW8gIgC1z0mmzcvME+H/DkT4W++oicHtsm6VdSRFEjDrUkBQ0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@1.1.6","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":450055},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"032f26edca6ece5071d2c4a9c88fbaaa1b592136","scripts":{"qmd":"tsx src/qmd.ts","test":"vitest run --reporter=verbose test/","build":"tsc -p tsconfig.build.json && printf '#!/usr/bin/env node\n' | cat - dist/qmd.js > dist/qmd.tmp && mv dist/qmd.tmp dist/qmd.js && chmod +x dist/qmd.js","index":"tsx src/qmd.ts index","rerank":"tsx src/qmd.ts rerank","search":"tsx src/qmd.ts search","vector":"tsx src/qmd.ts vector","prepare":"[ -d .git ] && ./scripts/install-hooks.sh || true","release":"./scripts/release.sh","vsearch":"tsx src/qmd.ts vsearch","inspector":"npx @modelcontextprotocol/inspector tsx src/qmd.ts mcp"},"_npmUser":{"name":"tobilu","email":"tobi@lutke.com"},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"10.9.4","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"22.22.0","dependencies":{"zod":"^4.2.1","yaml":"^2.8.2","fast-glob":"^3.3.0","picomatch":"^4.0.0","sqlite-vec":"^0.1.7-alpha.2","better-sqlite3":"^11.0.0","node-llama-cpp":"^3.17.1","@modelcontextprotocol/sdk":"^1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","vitest":"^3.0.0","@types/better-sqlite3":"^7.6.0"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"sqlite-vec-linux-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-x64":"^0.1.7-alpha.2","sqlite-vec-linux-arm64":"^0.1.7-alpha.2","sqlite-vec-windows-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-arm64":"^0.1.7-alpha.2"},"_npmOperationalInternal":{"tmp":"tmp/qmd_1.1.6_1773091625283_0.8997709643136489","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@tobilu/qmd","version":"2.0.0","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","_id":"@tobilu/qmd@2.0.0","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"bin/qmd"},"dist":{"shasum":"0817a64ecce14470f45d4f5cc66bf56eaba2baae","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-2.0.0.tgz","fileCount":23,"integrity":"sha512-zWJtlnWchEO1onw8orUYclsOMjiMRv9B1O0wRNMWaFRAi8Z60xdx/3rxcggrXxffGKrq/SUIHp+Z6O0Z+UGRCA==","signatures":[{"sig":"MEQCIG3XHDsvzKjShCRNfgmNhHuMWq3/2QoyQDXJKxcmGuOeAiAKooQ3rg3PPyWJy+k0Rcn3XB+x313YDUv59AHfMsRXjQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@2.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":488659},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"55f16460d03694e484e9fe4528d5929f4f834305","scripts":{"qmd":"tsx src/cli/qmd.ts","test":"vitest run --reporter=verbose test/","build":"tsc -p tsconfig.build.json && printf '#!/usr/bin/env node\n' | cat - dist/cli/qmd.js > dist/cli/qmd.tmp && mv dist/cli/qmd.tmp dist/cli/qmd.js && chmod +x dist/cli/qmd.js","index":"tsx src/cli/qmd.ts index","rerank":"tsx src/cli/qmd.ts rerank","search":"tsx src/cli/qmd.ts search","vector":"tsx src/cli/qmd.ts vector","prepare":"[ -d .git ] && ./scripts/install-hooks.sh || true","release":"./scripts/release.sh","vsearch":"tsx src/cli/qmd.ts vsearch","inspector":"npx @modelcontextprotocol/inspector tsx src/cli/qmd.ts mcp"},"_npmUser":{"name":"tobilu","email":"tobi@lutke.com"},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"10.9.4","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"22.22.0","dependencies":{"zod":"^4.2.1","yaml":"^2.8.2","fast-glob":"^3.3.0","picomatch":"^4.0.0","sqlite-vec":"^0.1.7-alpha.2","better-sqlite3":"^12.4.5","node-llama-cpp":"^3.17.1","@modelcontextprotocol/sdk":"^1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","vitest":"^3.0.0","@types/better-sqlite3":"^7.6.0"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"sqlite-vec-linux-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-x64":"^0.1.7-alpha.2","sqlite-vec-linux-arm64":"^0.1.7-alpha.2","sqlite-vec-windows-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-arm64":"^0.1.7-alpha.2"},"_npmOperationalInternal":{"tmp":"tmp/qmd_2.0.0_1773164135781_0.1530657442679213","host":"s3://npm-registry-packages-npm-production"}},"2.0.1":{"name":"@tobilu/qmd","version":"2.0.1","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","_id":"@tobilu/qmd@2.0.1","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"bin/qmd"},"dist":{"shasum":"68ebda98cce8239a96f41a3460e92bd8b0aa39ec","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-2.0.1.tgz","fileCount":25,"integrity":"sha512-v0fqksHGk2qSo8ztSshIB8Mkfdtdk+qKdKoEvsZ972fgvU7UCO0DSI+6xQMiSznBEbf89PRQVhz30yTIgRG3iw==","signatures":[{"sig":"MEUCIQCPJ4Bexcbq2pFhqXpYKkeMJErO0qJ0PjmfLQwkYtqcYwIgdH7T68R8gy25msM56ZYbXKPRe7k5zgtd1F86WbwH1r8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@2.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":504548},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"21a5dcc8538df59d08227c9d7cb8d6d8cdfd285d","scripts":{"qmd":"tsx src/cli/qmd.ts","test":"vitest run --reporter=verbose test/","build":"tsc -p tsconfig.build.json && printf '#!/usr/bin/env node\n' | cat - dist/cli/qmd.js > dist/cli/qmd.tmp && mv dist/cli/qmd.tmp dist/cli/qmd.js && chmod +x dist/cli/qmd.js","index":"tsx src/cli/qmd.ts index","rerank":"tsx src/cli/qmd.ts rerank","search":"tsx src/cli/qmd.ts search","vector":"tsx src/cli/qmd.ts vector","prepare":"[ -d .git ] && ./scripts/install-hooks.sh || true","release":"./scripts/release.sh","vsearch":"tsx src/cli/qmd.ts vsearch","inspector":"npx @modelcontextprotocol/inspector tsx src/cli/qmd.ts mcp"},"_npmUser":{"name":"tobilu","email":"tobi@lutke.com"},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"10.9.4","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"22.22.0","dependencies":{"zod":"^4.2.1","yaml":"^2.8.2","fast-glob":"^3.3.0","picomatch":"^4.0.0","sqlite-vec":"^0.1.7-alpha.2","better-sqlite3":"^12.4.5","node-llama-cpp":"^3.17.1","@modelcontextprotocol/sdk":"^1.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","vitest":"^3.0.0","@types/better-sqlite3":"^7.6.0"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"sqlite-vec-linux-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-x64":"^0.1.7-alpha.2","sqlite-vec-linux-arm64":"^0.1.7-alpha.2","sqlite-vec-windows-x64":"^0.1.7-alpha.2","sqlite-vec-darwin-arm64":"^0.1.7-alpha.2"},"_npmOperationalInternal":{"tmp":"tmp/qmd_2.0.1_1773190993090_0.008488019542222558","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@tobilu/qmd","version":"2.1.0","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","_id":"@tobilu/qmd@2.1.0","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"bin/qmd"},"dist":{"shasum":"f31c7319cda59c76f3ae5ba240bfb973840df51a","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-2.1.0.tgz","fileCount":33,"integrity":"sha512-oOuCoiWL9R2urgj0C336Qpv8qpq1SHhx80Vg5cQo+QRB26XPXqy7MTg/CWS6WAe6ruvVul7kEv21M5YhRzreAw==","signatures":[{"sig":"MEYCIQCHW2x38uiLjf+JjYtCmKBcjT+8tuw3vQpG5neyBxImZwIhAK5uA7N24uooBjEvXOoy/G7RK7T6sSmG3uCEF54pxF+a","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@2.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":571224},"main":"dist/index.js","pnpm":{"onlyBuiltDependencies":["better-sqlite3","esbuild","node-llama-cpp","tree-sitter-go","tree-sitter-javascript","tree-sitter-python","tree-sitter-rust","tree-sitter-typescript"]},"type":"module","types":"dist/index.d.ts","engines":{"node":">=22.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"65cd1b3fd02891d1ee0eefa751620918664fa321","scripts":{"qmd":"tsx src/cli/qmd.ts","test":"vitest run --reporter=verbose test/","build":"tsc -p tsconfig.build.json && printf '#!/usr/bin/env node\n' | cat - dist/cli/qmd.js > dist/cli/qmd.tmp && mv dist/cli/qmd.tmp dist/cli/qmd.js && chmod +x dist/cli/qmd.js","index":"tsx src/cli/qmd.ts index","rerank":"tsx src/cli/qmd.ts rerank","search":"tsx src/cli/qmd.ts search","vector":"tsx src/cli/qmd.ts vector","prepare":"[ -d .git ] && ./scripts/install-hooks.sh || true","release":"./scripts/release.sh","vsearch":"tsx src/cli/qmd.ts vsearch","inspector":"npx @modelcontextprotocol/inspector tsx src/cli/qmd.ts mcp"},"_npmUser":{"name":"tobilu","email":"tobi@lutke.com"},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"10.9.7","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"22.22.2","dependencies":{"zod":"4.2.1","yaml":"2.8.3","fast-glob":"3.3.3","picomatch":"4.0.4","sqlite-vec":"0.1.9","better-sqlite3":"12.8.0","node-llama-cpp":"3.18.1","web-tree-sitter":"0.26.7","@modelcontextprotocol/sdk":"1.29.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"4.21.0","vitest":"3.2.4","@types/better-sqlite3":"7.6.13"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"tree-sitter-go":"0.23.4","tree-sitter-rust":"0.24.0","tree-sitter-python":"0.23.4","sqlite-vec-linux-x64":"0.1.9","sqlite-vec-darwin-x64":"0.1.9","sqlite-vec-linux-arm64":"0.1.9","sqlite-vec-windows-x64":"0.1.9","tree-sitter-typescript":"0.23.2","sqlite-vec-darwin-arm64":"0.1.9"},"_npmOperationalInternal":{"tmp":"tmp/qmd_2.1.0_1775429747094_0.9677034130666933","host":"s3://npm-registry-packages-npm-production"}},"2.5.1":{"name":"@tobilu/qmd","version":"2.5.1","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","_id":"@tobilu/qmd@2.5.1","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"bin/qmd"},"dist":{"shasum":"84a2c2a9f370840681ee0d85884b6084d92368c0","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-2.5.1.tgz","fileCount":41,"integrity":"sha512-Ep9ccOj1bNRinfTIszp5UZP8xfi5AJNtmzwWDD4ZVm2YdWVS+rFobWJQovj0HD2uIAFrryvbSpZYeGa3flEO7g==","signatures":[{"sig":"MEYCIQDG5nyGcB+4MCs9vWxVriPuO+pd07FiX/8USVuR3XtgQAIhAMp1qJ5+fLo12FtERBW+DTq4IfqNiEpsdYZvdWMzaq7a","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@2.5.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":698748},"main":"dist/index.js","pnpm":{"onlyBuiltDependencies":["better-sqlite3","esbuild","node-llama-cpp","tree-sitter-go","tree-sitter-javascript","tree-sitter-python","tree-sitter-rust","tree-sitter-typescript"]},"type":"module","types":"dist/index.d.ts","engines":{"node":">=22.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"ba6538090fbd929e835c1aafaa75ab1ede60682c","scripts":{"qmd":"tsx src/cli/qmd.ts","test":"node scripts/test-all.mjs","build":"node scripts/build.mjs","index":"tsx src/cli/qmd.ts index","rerank":"tsx src/cli/qmd.ts rerank","search":"tsx src/cli/qmd.ts search","vector":"tsx src/cli/qmd.ts vector","prepare":"[ -d .git ] && ./scripts/install-hooks.sh || true","release":"./scripts/release.sh","vsearch":"tsx src/cli/qmd.ts vsearch","test:bun":"bun test --timeout 60000 --preload ./src/test-preload.ts","inspector":"npx @modelcontextprotocol/inspector tsx src/cli/qmd.ts mcp","test:node":"node ./node_modules/vitest/vitest.mjs run --reporter=verbose --testTimeout 60000","test:unit":"CI=true node ./node_modules/vitest/vitest.mjs run --reporter=verbose --testTimeout 60000 test/ && CI=true bun test --timeout 60000 --preload ./src/test-preload.ts test/","test:types":"node ./node_modules/typescript/bin/tsc -p tsconfig.build.json --noEmit","test:package":"node scripts/package-smoke.mjs","smoke:package-grammars":"node scripts/check-package-grammars.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:e6c4aec0-27ea-47d9-99dd-7b537d96ce18"}},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"11.12.1","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"24.15.0","dependencies":{"zod":"4.2.1","yaml":"2.9.0","fast-glob":"3.3.3","picomatch":"4.0.4","sqlite-vec":"0.1.9","better-sqlite3":"12.10.0","node-llama-cpp":"3.18.1","tree-sitter-go":"0.25.0","web-tree-sitter":"0.26.8","tree-sitter-rust":"0.24.0","tree-sitter-python":"0.25.0","tree-sitter-typescript":"0.23.2","@modelcontextprotocol/sdk":"1.29.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"4.21.0","vitest":"3.2.4","@types/better-sqlite3":"7.6.13"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"sqlite-vec-linux-x64":"0.1.9","sqlite-vec-darwin-x64":"0.1.9","sqlite-vec-linux-arm64":"0.1.9","sqlite-vec-windows-x64":"0.1.9","sqlite-vec-darwin-arm64":"0.1.9"},"_npmOperationalInternal":{"tmp":"tmp/qmd_2.5.1_1779236879936_0.6680236988099062","host":"s3://npm-registry-packages-npm-production"}},"2.5.2":{"name":"@tobilu/qmd","version":"2.5.2","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","_id":"@tobilu/qmd@2.5.2","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"bin/qmd"},"dist":{"shasum":"5643b2f0949553836a46972b7d550a457479c2e4","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-2.5.2.tgz","fileCount":41,"integrity":"sha512-kuF+6eGdpWAsgYnRB7O8AEp5XpgEbagTuzcZPE8NbtGjPpWWhb4f35DkoV31lyfe5Ip2RVL6jZoTTCYndu7o/g==","signatures":[{"sig":"MEYCIQDXRKXpvdRjEmUlxbgZWpHSFpcQ0b76EwlvSh/mHZha4gIhAJyaLZ40BTB150T/uF9r03Dm0ip0IeaOY+fsUXuIYNXC","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@2.5.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":700816},"main":"dist/index.js","pnpm":{"onlyBuiltDependencies":["better-sqlite3","esbuild","node-llama-cpp","tree-sitter-go","tree-sitter-javascript","tree-sitter-python","tree-sitter-rust","tree-sitter-typescript"]},"type":"module","types":"dist/index.d.ts","engines":{"node":">=22.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"443760f4d5a17550d77a0e3146b5b8f08452991f","scripts":{"qmd":"tsx src/cli/qmd.ts","test":"node scripts/test-all.mjs","build":"node scripts/build.mjs","index":"tsx src/cli/qmd.ts index","rerank":"tsx src/cli/qmd.ts rerank","search":"tsx src/cli/qmd.ts search","vector":"tsx src/cli/qmd.ts vector","prepare":"[ -d .git ] && ./scripts/install-hooks.sh || true","release":"./scripts/release.sh","vsearch":"tsx src/cli/qmd.ts vsearch","test:bun":"bun test --timeout 60000 --preload ./src/test-preload.ts","inspector":"npx @modelcontextprotocol/inspector tsx src/cli/qmd.ts mcp","test:node":"node ./node_modules/vitest/vitest.mjs run --reporter=verbose --testTimeout 60000","test:unit":"CI=true node ./node_modules/vitest/vitest.mjs run --reporter=verbose --testTimeout 60000 test/ && CI=true bun test --timeout 60000 --preload ./src/test-preload.ts test/","test:types":"node ./node_modules/typescript/bin/tsc -p tsconfig.build.json --noEmit","test:package":"node scripts/package-smoke.mjs","smoke:package-grammars":"node scripts/check-package-grammars.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:e6c4aec0-27ea-47d9-99dd-7b537d96ce18"}},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"11.12.1","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"24.15.0","dependencies":{"zod":"4.2.1","yaml":"2.9.0","fast-glob":"3.3.3","picomatch":"4.0.4","sqlite-vec":"0.1.9","better-sqlite3":"12.10.0","node-llama-cpp":"3.18.1","tree-sitter-go":"0.25.0","web-tree-sitter":"0.26.8","tree-sitter-rust":"0.24.0","tree-sitter-python":"0.25.0","tree-sitter-typescript":"0.23.2","@modelcontextprotocol/sdk":"1.29.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"4.21.0","vitest":"3.2.4","@types/better-sqlite3":"7.6.13"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"sqlite-vec-linux-x64":"0.1.9","sqlite-vec-darwin-x64":"0.1.9","sqlite-vec-linux-arm64":"0.1.9","sqlite-vec-windows-x64":"0.1.9","sqlite-vec-darwin-arm64":"0.1.9"},"_npmOperationalInternal":{"tmp":"tmp/qmd_2.5.2_1779481518381_0.36541700666790344","host":"s3://npm-registry-packages-npm-production"}},"2.5.3":{"name":"@tobilu/qmd","version":"2.5.3","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","_id":"@tobilu/qmd@2.5.3","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"bin":{"qmd":"bin/qmd"},"dist":{"shasum":"6b282bd7982ce6e50e7110696cd186e6828e9b1f","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-2.5.3.tgz","fileCount":41,"integrity":"sha512-wUKc4pSPDbgs7mV7JYE8/Qj1pNXXatJFV8byTT/T3yLaoAXheFtWu0BgSWwoWGhRkMmxl5Qyitt66NHgbMyeBA==","signatures":[{"sig":"MEUCIQCz2lImTuzc+zvYPfhbSraMhGBZzYXcohF4po9gosOH4AIgIsgFQPvH6cIbXbu23/fxsDC95flegtLUs6vdjA5q20U=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@2.5.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":732385},"main":"dist/index.js","pnpm":{"onlyBuiltDependencies":["better-sqlite3","esbuild","node-llama-cpp","tree-sitter-go","tree-sitter-javascript","tree-sitter-python","tree-sitter-rust","tree-sitter-typescript"]},"type":"module","types":"dist/index.d.ts","engines":{"node":">=22.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"53232770867ccb16538c2c6034e7d891dffc9ce3","scripts":{"qmd":"tsx src/cli/qmd.ts","test":"node scripts/test-all.mjs","build":"node scripts/build.mjs","index":"tsx src/cli/qmd.ts index","rerank":"tsx src/cli/qmd.ts rerank","search":"tsx src/cli/qmd.ts search","vector":"tsx src/cli/qmd.ts vector","prepare":"[ -d .git ] && ./scripts/install-hooks.sh || true","release":"./scripts/release.sh","vsearch":"tsx src/cli/qmd.ts vsearch","test:bun":"bun test --timeout 60000 --preload ./src/test-preload.ts","inspector":"npx @modelcontextprotocol/inspector tsx src/cli/qmd.ts mcp","test:node":"node ./node_modules/vitest/vitest.mjs run --reporter=verbose --testTimeout 60000","test:unit":"CI=true node ./node_modules/vitest/vitest.mjs run --reporter=verbose --testTimeout 60000 test/ && CI=true bun test --timeout 60000 --preload ./src/test-preload.ts test/","test:types":"node ./node_modules/typescript/bin/tsc -p tsconfig.build.json --noEmit","test:package":"node scripts/package-smoke.mjs","smoke:package-grammars":"node scripts/check-package-grammars.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:e6c4aec0-27ea-47d9-99dd-7b537d96ce18"}},"repository":{"url":"git+https://github.com/tobi/qmd.git","type":"git"},"_npmVersion":"11.13.0","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","directories":{},"_nodeVersion":"24.16.0","dependencies":{"zod":"4.2.1","yaml":"2.9.0","fast-glob":"3.3.3","picomatch":"4.0.4","sqlite-vec":"0.1.9","better-sqlite3":"12.10.0","node-llama-cpp":"3.18.1","tree-sitter-go":"0.25.0","web-tree-sitter":"0.26.8","tree-sitter-rust":"0.24.0","tree-sitter-python":"0.25.0","tree-sitter-typescript":"0.23.2","@modelcontextprotocol/sdk":"1.29.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"4.21.0","vitest":"3.2.4","@types/better-sqlite3":"7.6.13"},"peerDependencies":{"typescript":"^5.9.3"},"optionalDependencies":{"sqlite-vec-linux-x64":"0.1.9","sqlite-vec-darwin-x64":"0.1.9","sqlite-vec-linux-arm64":"0.1.9","sqlite-vec-windows-x64":"0.1.9","sqlite-vec-darwin-arm64":"0.1.9"},"_npmOperationalInternal":{"tmp":"tmp/qmd_2.5.3_1780025059248_0.9054748020658505","host":"s3://npm-registry-packages-npm-production"}},"2.8.3":{"name":"@tobilu/qmd","version":"2.8.3","description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","type":"module","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"bin":{"qmd":"bin/qmd"},"scripts":{"prepare":"node scripts/install-hooks.mjs && node scripts/build.mjs","build":"node scripts/build.mjs","test":"node scripts/test-all.mjs","test:types":"node ./node_modules/typescript/bin/tsc -p tsconfig.build.json --noEmit","test:node":"node ./node_modules/vitest/vitest.mjs run --reporter=verbose --testTimeout 60000","test:bun":"bun test --timeout 60000 --preload ./src/test-preload.ts","test:unit":"CI=true node ./node_modules/vitest/vitest.mjs run --reporter=verbose --testTimeout 60000 test/ && CI=true bun test --timeout 60000 --preload ./src/test-preload.ts test/","test:package":"node scripts/package-smoke.mjs","qmd":"tsx src/cli/qmd.ts","index":"tsx src/cli/qmd.ts index","vector":"tsx src/cli/qmd.ts vector","search":"tsx src/cli/qmd.ts search","vsearch":"tsx src/cli/qmd.ts vsearch","rerank":"tsx src/cli/qmd.ts rerank","inspector":"npx @modelcontextprotocol/inspector tsx src/cli/qmd.ts mcp","release":"./scripts/release.sh","smoke:package-grammars":"node scripts/check-package-grammars.mjs"},"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/tobi/qmd.git"},"homepage":"https://github.com/tobi/qmd#readme","bugs":{"url":"https://github.com/tobi/qmd/issues"},"dependencies":{"@modelcontextprotocol/server":"2.0.0","better-sqlite3":"^13.0.3","fast-glob":"3.3.3","node-llama-cpp":"3.20.0","picomatch":"4.0.5","sqlite-vec":"0.1.9","tree-sitter-go":"0.25.0","tree-sitter-python":"0.25.0","tree-sitter-rust":"0.24.0","tree-sitter-typescript":"0.23.2","web-tree-sitter":"0.26.12","yaml":"2.9.0","zod":"4.2.1"},"optionalDependencies":{"sqlite-vec-darwin-arm64":"0.1.9","sqlite-vec-darwin-x64":"0.1.9","sqlite-vec-linux-arm64":"0.1.9","sqlite-vec-linux-x64":"0.1.9","sqlite-vec-windows-x64":"0.1.9"},"devDependencies":{"@types/better-sqlite3":"7.6.13","tsx":"4.23.12","vitest":"3.2.7"},"pnpm":{"onlyBuiltDependencies":["better-sqlite3","esbuild","node-llama-cpp","tree-sitter-go","tree-sitter-python","tree-sitter-rust","tree-sitter-typescript"]},"peerDependencies":{"typescript":"^5.9.3"},"engines":{"node":">=22.0.0"},"keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","gitHead":"facd35e01359e59d938bc9418e93fb9318addee3","_id":"@tobilu/qmd@2.8.3","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-zjfVwrObPB618B6x8SdhlGv/tX9OxRHsbQnr5DUtBvqPK6HGQ27lM+9/BAY5okpjrHVnW56hLyDkqoTcsrVLzA==","shasum":"7e1515b1daf349a1dc88cff53da925d34f89c948","tarball":"https://registry.npmjs.org/@tobilu/qmd/-/qmd-2.8.3.tgz","fileCount":53,"unpackedSize":914522,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@tobilu%2fqmd@2.8.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHW8DfXOgj/MyuB0/oMufSYUoShkgbR0HnWDUUfbWouaAiEAnZg04j9W3ygnyMjyQ/CaYBPYo9ZgoDS0iIQ8HsVCXck="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:e6c4aec0-27ea-47d9-99dd-7b537d96ce18"}},"directories":{},"maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/qmd_2.8.3_1786919432494_0.7097457050364584"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-15T19:13:40.744Z","modified":"2026-08-16T22:30:32.974Z","0.9.0":"2026-02-15T19:13:41.042Z","1.0.0":"2026-02-16T03:08:41.707Z","1.0.5":"2026-02-16T12:58:59.566Z","1.0.6":"2026-02-16T14:39:35.369Z","1.0.7":"2026-02-18T19:56:54.450Z","1.1.1":"2026-03-07T18:12:52.184Z","1.1.2":"2026-03-07T20:01:57.092Z","1.1.5":"2026-03-08T01:37:41.973Z","1.1.6":"2026-03-09T21:27:05.425Z","2.0.0":"2026-03-10T17:35:35.937Z","2.0.1":"2026-03-11T01:03:13.295Z","2.1.0":"2026-04-05T22:55:47.281Z","2.5.1":"2026-05-20T00:28:00.109Z","2.5.2":"2026-05-22T20:25:18.550Z","2.5.3":"2026-05-29T03:24:19.511Z","2.8.3":"2026-08-16T22:30:32.642Z"},"bugs":{"url":"https://github.com/tobi/qmd/issues"},"author":{"name":"Tobi Lutke","email":"tobi@lutke.com"},"license":"MIT","homepage":"https://github.com/tobi/qmd#readme","keywords":["markdown","search","fts","full-text-search","vector","semantic-search","sqlite","bm25","embeddings","rag","mcp","reranking","knowledge-base","local-ai","llm"],"repository":{"type":"git","url":"git+https://github.com/tobi/qmd.git"},"description":"Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking","maintainers":[{"name":"tobilu","email":"tobi@lutke.com"}],"readme":"# QMD - Query Markup Documents\n\nAn on-device search engine for everything you need to remember. Index your markdown notes, meeting transcripts, documentation, and knowledge bases. Search with keywords or natural language. Ideal for your agentic flows.\n\nQMD combines BM25 full-text search, vector semantic search, and LLM re-ranking—all running locally via node-llama-cpp with GGUF models.\n\n```mermaid\nflowchart LR\n  Q[User Query] --> X[Query Expansion]\n  Q --> FTS[BM25 Search]\n  Q --> VS[Vector Search]\n  X --> HYDE[HyDE]\n  X --> VEC[Vec dense sentences]\n  X --> LEX[Lex BM25 keywords]\n  HYDE --> VS\n  VEC --> VS\n  LEX --> FTS\n  VS --> RRF[Reciprocal Rank Fusion]\n  FTS --> RRF\n  RRF --> RR[LLM Reranker]\n  RR --> OUT[Final ranked results]\n```\n\nTyped expansions are routed exclusively: `lex` → BM25/FTS, `vec` and `hyde` → vector search. The original query is sent to both backends, then fused with RRF and reranked.\n\nYou can read more about QMD's progress in the [CHANGELOG](CHANGELOG.md).\n\n## Quick Start\n\n```sh\n# Install globally (Node or Bun)\nnpm install -g @tobilu/qmd\n# or\nbun install -g @tobilu/qmd\n\n# Or run directly\nnpx @tobilu/qmd ...\nbunx @tobilu/qmd ...\n\n# Create collections for your notes, docs, and meeting transcripts\nqmd collection add ~/notes --name notes\nqmd collection add ~/Documents/meetings --name meetings\nqmd collection add ~/work/docs --name docs\n\n# Add context to help with search results, each piece of context will be returned when matching sub documents are returned. This works as a tree. This is the key feature of QMD as it allows LLMs to make much better contextual choices when selecting documents. Don't sleep on it!\nqmd context add qmd://notes \"Personal notes and ideas\"\nqmd context add qmd://meetings \"Meeting transcripts and notes\"\nqmd context add qmd://docs \"Work documentation\"\n\n# Generate embeddings for semantic search\nqmd embed\n\n# Search across everything\nqmd search \"project timeline\"           # Fast keyword search\nqmd vsearch \"how to deploy\"             # Semantic search\nqmd query \"quarterly planning process\"  # Hybrid + reranking (best quality)\n\n# Get a specific document\nqmd get \"meetings/2024-01-15.md\"\n\n# Get a document by docid (shown in search results)\nqmd get \"#abc123\"\n\n# Get multiple documents by glob pattern\nqmd multi-get \"journals/2025-05*.md\"\n\n# Search within a specific collection\nqmd search \"API\" -c notes\n\n# Export all matches for an agent\nqmd search \"API\" --all --files --min-score 0.3\n```\n\n### Using with AI Agents\n\nQMD's `--json` and `--files` output formats are designed for agentic workflows:\n\n```sh\n# Get structured results for an LLM\nqmd search \"authentication\" --json -n 10\n\n# List all relevant files above a threshold\nqmd query \"error handling\" --all --files --min-score 0.4\n\n# Retrieve full document content\nqmd get \"docs/api-reference.md\" --full\n```\n\n### MCP Server\n\nAlthough the tool works perfectly fine when you just tell your agent to use it on the command line, it also exposes an MCP (Model Context Protocol) server for tighter integration.\n\n**Tools exposed:**\n- `query` — Search with typed sub-queries (`lex`/`vec`/`hyde`), combined via RRF + reranking\n- `get` — Retrieve a document by path or docid (with fuzzy matching suggestions)\n- `multi_get` — Batch retrieve by glob pattern, comma-separated list, or docids\n- `status` — Index health and collection info\n\n**Claude Desktop configuration** (`~/Library/Application Support/Claude/claude_desktop_config.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"qmd\": {\n      \"command\": \"qmd\",\n      \"args\": [\"mcp\"]\n    }\n  }\n}\n```\n\n**Claude Code** — Install the plugin (recommended):\n\n```bash\nclaude plugin marketplace add tobi/qmd\nclaude plugin install qmd@qmd\n```\n\nOr configure MCP manually in `~/.claude/settings.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"qmd\": {\n      \"command\": \"qmd\",\n      \"args\": [\"mcp\"]\n    }\n  }\n}\n```\n\n#### HTTP Transport\n\nBy default, QMD's MCP server uses stdio (launched as a subprocess by each client). For a shared, long-lived server that avoids repeated model loading, use the HTTP transport:\n\n```sh\n# Foreground (Ctrl-C to stop)\nqmd mcp --http                    # localhost:8181\nqmd mcp --http --port 8080        # custom port\nqmd mcp --http --host 0.0.0.0     # bind all interfaces (e.g. container probes)\n\n# Background daemon\nqmd mcp --http --daemon           # start, writes PID to ~/.cache/qmd/mcp.pid\nqmd mcp stop                      # stop via PID file\nqmd status                        # shows \"MCP: running (PID ...)\" when active\n```\n\nThe server binds to `localhost` by default. Pass `--host` (or set the `QMD_HOST`\nenvironment variable) to override — `--host 0.0.0.0` is useful when the server\nruns in a container and a liveness probe connects from a non-loopback address.\n\nThe HTTP server exposes two endpoints:\n- `POST /mcp` — MCP Streamable HTTP (JSON responses, stateless)\n- `POST /query` (alias `/search`) — structured search without the MCP protocol\n- `GET /health` — liveness check with uptime\n\n\n##### Origin and Host validation\n\nEvery request is screened before routing: a request carrying an `Origin` header\nthat does not name a loopback address is rejected with `403`, as is a `Host`\nheader naming something other than the address the server is bound to. This is\nwhat stops a web page you visit from reading your index through DNS rebinding —\nloopback binding alone does not, since the browser makes the request from your\nown machine.\n\nRequests without an `Origin` header — curl, MCP clients, editors — are\nunaffected, which covers every normal local client.\n\n| Variable | Effect |\n|----------|--------|\n| `QMD_ALLOWED_ORIGINS` | Comma-separated origins to accept in addition to loopback, e.g. `https://notes.internal`. Set to `*` to disable the check entirely. |\n| `QMD_ALLOWED_HOSTS` | Comma-separated `Host` values to accept in addition to loopback and the bind address. |\n\n`--host 0.0.0.0` cannot know which `Host` values are legitimate, so it skips the\nhost check and warns at startup. Set `QMD_ALLOWED_HOSTS` to re-enable it, and\nremember the endpoints are unauthenticated — put your own auth in front of a\nserver that is reachable off-host.\n\nLLM models stay loaded in VRAM across requests. Embedding/reranking contexts are disposed after 5 min idle and transparently recreated on the next request (~1s penalty, models remain loaded).\n\nPoint any MCP client at `http://localhost:8181/mcp` to connect.\n\n#### MCP Tool Parameters\n\n| Tool | Parameter | Type | Notes |\n|------|-----------|------|-------|\n| `query` | `searches` | array | Typed sub-queries (`lex`/`vec`/`hyde`), 1–10. **Required.** First gets 2x weight. |\n| `query` | `collections` | string[] | Filter by collection names (OR). **Array only** — singular `collection` is silently ignored. |\n| `query` | `intent` | string | Disambiguation context (does not search on its own) |\n| `query` | `limit` | number | Max results (default 10) |\n| `query` | `minScore` | number | Minimum relevance 0–1 (default 0) |\n| `query` | `candidateLimit` | number | Max candidates to rerank (default 40) |\n| `query` | `rerank` | boolean | Run LLM reranking (default **true**); set false for RRF-only |\n| `get` | `file` | string | Path, docid (`#abc123`), or `path:from:count` (e.g. `#abc123:120:40`) |\n| `get` | `fromLine` | number | Start line (1-indexed); overrides the `:from` suffix |\n| `get` | `maxLines` | number | Limit returned lines |\n| `get` | `lineNumbers` | boolean | Prefix lines with numbers (default **true**) |\n| `multi_get` | `pattern` | string | Glob pattern or comma-separated list |\n| `multi_get` | `maxBytes` | number | Skip files larger than N (default 10240) |\n| `multi_get` | `maxLines` | number | Limit lines per file |\n| `multi_get` | `lineNumbers` | boolean | Prefix lines with numbers (default **true**) |\n\nUnknown parameters are silently ignored (not rejected) — double-check names if\nresults seem unscoped. The HTTP `/query` and `/search` endpoints return\n`qmd://collection/path` URIs in the `file` field, matching the CLI and MCP output.\n\n### SDK / Library Usage\n\nUse QMD as a library in your own Node.js or Bun applications.\n\n#### Installation\n\n```sh\nnpm install @tobilu/qmd\n```\n\n#### Quick Start\n\n```typescript\nimport { createStore } from '@tobilu/qmd'\n\nconst store = await createStore({\n  dbPath: './my-index.sqlite',\n  config: {\n    collections: {\n      docs: { path: '/path/to/docs', pattern: '**/*.md' },\n    },\n  },\n})\n\nconst results = await store.search({ query: \"authentication flow\" })\nconsole.log(results.map(r => `${r.title} (${Math.round(r.score * 100)}%)`))\n\nawait store.close()\n```\n\n#### Store Creation\n\n`createStore()` accepts three modes:\n\n```typescript\nimport { createStore } from '@tobilu/qmd'\n\n// 1. Inline config — no files needed besides the DB\nconst store = await createStore({\n  dbPath: './index.sqlite',\n  config: {\n    collections: {\n      docs: { path: '/path/to/docs', pattern: '**/*.md' },\n      notes: { path: '/path/to/notes' },\n    },\n  },\n})\n\n// 2. YAML config file — collections defined in a file\nconst store2 = await createStore({\n  dbPath: './index.sqlite',\n  configPath: './qmd.yml',\n})\n\n// 3. DB-only — reopen a previously configured store\nconst store3 = await createStore({ dbPath: './index.sqlite' })\n```\n\n#### Search\n\nThe unified `search()` method handles both simple queries and pre-expanded structured queries:\n\n```typescript\n// Simple query — auto-expanded via LLM, then BM25 + vector + reranking\nconst results = await store.search({ query: \"authentication flow\" })\n\n// With options\nconst results2 = await store.search({\n  query: \"rate limiting\",\n  intent: \"API throttling and abuse prevention\",\n  collection: \"docs\",\n  limit: 5,\n  minScore: 0.3,\n  explain: true,\n})\n\n// Pre-expanded queries — skip auto-expansion, control each sub-query\nconst results3 = await store.search({\n  queries: [\n    { type: 'lex', query: '\"connection pool\" timeout -redis' },\n    { type: 'vec', query: 'why do database connections time out under load' },\n  ],\n  collections: [\"docs\", \"notes\"],\n})\n\n// Skip reranking for faster results\nconst fast = await store.search({ query: \"auth\", rerank: false })\n```\n\nFor direct backend access:\n\n```typescript\n// BM25 keyword search (fast, no LLM)\nconst lexResults = await store.searchLex(\"auth middleware\", { limit: 10 })\n\n// Vector similarity search (embedding model, no reranking)\nconst vecResults = await store.searchVector(\"how users log in\", { limit: 10 })\n\n// Manual query expansion for full control\nconst expanded = await store.expandQuery(\"auth flow\", { intent: \"user login\" })\nconst results4 = await store.search({ queries: expanded })\n```\n\n#### Retrieval\n\n```typescript\n// Get a document by path or docid\nconst doc = await store.get(\"docs/readme.md\")\nconst byId = await store.get(\"#abc123\")\n\nif (!(\"error\" in doc)) {\n  console.log(doc.title, doc.displayPath, doc.context)\n}\n\n// Get document body with line range\nconst body = await store.getDocumentBody(\"docs/readme.md\", {\n  fromLine: 50,\n  maxLines: 100,\n})\n\n// Batch retrieve by glob or comma-separated list\nconst { docs, errors } = await store.multiGet(\"docs/**/*.md\", {\n  maxBytes: 20480,\n})\n```\n\n#### Collections\n\n```typescript\n// Add a collection\nawait store.addCollection(\"myapp\", {\n  path: \"/src/myapp\",\n  pattern: \"**/*.ts\",\n  ignore: [\"node_modules/**\", \"*.test.ts\"],\n})\n\n// List collections with document stats\nconst collections = await store.listCollections()\n// => [{ name, pwd, glob_pattern, doc_count, active_count, last_modified, includeByDefault }]\n\n// Get names of collections included in queries by default\nconst defaults = await store.getDefaultCollectionNames()\n\n// Remove / rename\nawait store.removeCollection(\"myapp\")\nawait store.renameCollection(\"old-name\", \"new-name\")\n```\n\n#### Context\n\nContext adds descriptive metadata that improves search relevance and is returned alongside results:\n\n```typescript\n// Add context for a path within a collection\nawait store.addContext(\"docs\", \"/api\", \"REST API reference documentation\")\n\n// Set global context (applies to all collections)\nawait store.setGlobalContext(\"Internal engineering documentation\")\n\n// List all contexts\nconst contexts = await store.listContexts()\n// => [{ collection, path, context }]\n\n// Remove context\nawait store.removeContext(\"docs\", \"/api\")\nawait store.setGlobalContext(undefined)  // clear global\n```\n\n#### Indexing\n\n```typescript\n// Re-index collections by scanning the filesystem\nconst result = await store.update({\n  collections: [\"docs\"],  // optional — defaults to all\n  onProgress: ({ collection, file, current, total }) => {\n    console.log(`[${collection}] ${current}/${total} ${file}`)\n  },\n})\n// => { collections, indexed, updated, unchanged, removed, needsEmbedding }\n\n// Generate vector embeddings\nconst embedResult = await store.embed({\n  force: false,           // true to re-embed everything\n  chunkStrategy: \"auto\",  // \"regex\" (default) or \"auto\" (AST for code files)\n  onProgress: ({ current, total, collection }) => {\n    console.log(`Embedding ${current}/${total}`)\n  },\n})\n```\n\n#### Types\n\nKey types exported for SDK consumers:\n\n```typescript\nimport type {\n  QMDStore,            // The store interface\n  SearchOptions,       // Options for search()\n  LexSearchOptions,    // Options for searchLex()\n  VectorSearchOptions, // Options for searchVector()\n  HybridQueryResult,   // Search result with score, snippet, context\n  SearchResult,        // Result from searchLex/searchVector\n  ExpandedQuery,       // Typed sub-query { type: 'lex'|'vec'|'hyde', query }\n  DocumentResult,      // Document metadata + body\n  DocumentNotFound,    // Error with similarFiles suggestions\n  MultiGetResult,      // Batch retrieval result\n  UpdateProgress,      // Progress callback info for update()\n  UpdateResult,        // Aggregated update result\n  EmbedProgress,       // Progress callback info for embed()\n  EmbedResult,         // Embedding result\n  StoreOptions,        // createStore() options\n  CollectionConfig,    // Inline config shape\n  IndexStatus,         // From getStatus()\n  IndexHealthInfo,     // From getIndexHealth()\n} from '@tobilu/qmd'\n```\n\nUtility exports:\n\n```typescript\nimport {\n  extractSnippet,              // Extract a relevant snippet from text\n  addLineNumbers,              // Add line numbers to text\n  DEFAULT_MULTI_GET_MAX_BYTES, // Default max file size for multiGet (64KB)\n  Maintenance,                 // Database maintenance operations\n} from '@tobilu/qmd'\n```\n\n#### Lifecycle\n\n```typescript\n// Close the store — disposes LLM models and DB connection\nawait store.close()\n```\n\nThe SDK requires explicit `dbPath` — no defaults are assumed. This makes it safe to embed in any application without side effects.\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                         QMD Hybrid Search Pipeline                          │\n└─────────────────────────────────────────────────────────────────────────────┘\n\n                              ┌─────────────────┐\n                              │   User Query    │\n                              └────────┬────────┘\n                                       │\n                        ┌──────────────┴──────────────┐\n                        ▼                             ▼\n               ┌────────────────┐            ┌────────────────┐\n               │ Query Expansion│            │  Original Query│\n               │  (fine-tuned)  │            │   (×2 weight)  │\n               └───────┬────────┘            └───────┬────────┘\n                       │                             │\n                       │ 2 alternative queries       │\n                       └──────────────┬──────────────┘\n                                      │\n              ┌───────────────────────┼───────────────────────┐\n              ▼                       ▼                       ▼\n     ┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐\n     │ Original Query  │     │ Expanded Query 1│     │ Expanded Query 2│\n     └────────┬────────┘     └────────┬────────┘     └────────┬────────┘\n              │                       │                       │\n      ┌───────┴───────┐       ┌───────┴───────┐       ┌───────┴───────┐\n      ▼               ▼       ▼               ▼       ▼               ▼\n  ┌───────┐       ┌───────┐ ┌───────┐     ┌───────┐ ┌───────┐     ┌───────┐\n  │ BM25  │       │Vector │ │ BM25  │     │Vector │ │ BM25  │     │Vector │\n  │(FTS5) │       │Search │ │(FTS5) │     │Search │ │(FTS5) │     │Search │\n  └───┬───┘       └───┬───┘ └───┬───┘     └───┬───┘ └───┬───┘     └───┬───┘\n      │               │         │             │         │             │\n      └───────┬───────┘         └──────┬──────┘         └──────┬──────┘\n              │                        │                       │\n              └────────────────────────┼───────────────────────┘\n                                       │\n                                       ▼\n                          ┌───────────────────────┐\n                          │   RRF Fusion + Bonus  │\n                          │  Original query: ×2   │\n                          │  Top-rank bonus: +0.05│\n                          │     Top 30 Kept       │\n                          └───────────┬───────────┘\n                                      │\n                                      ▼\n                          ┌───────────────────────┐\n                          │    LLM Re-ranking     │\n                          │  (qwen3-reranker)     │\n                          │  Yes/No + logprobs    │\n                          └───────────┬───────────┘\n                                      │\n                                      ▼\n                          ┌───────────────────────┐\n                          │  Position-Aware Blend │\n                          │  Top 1-3:  75% RRF    │\n                          │  Top 4-10: 60% RRF    │\n                          │  Top 11+:  40% RRF    │\n                          └───────────────────────┘\n```\n\n## Score Normalization & Fusion\n\n### Search Backends\n\n| Backend | Raw Score | Conversion | Range |\n|---------|-----------|------------|-------|\n| **FTS (BM25)** | SQLite FTS5 BM25 | `Math.abs(score)` | 0 to ~25+ |\n| **Vector** | Cosine distance | `1 / (1 + distance)` | 0.0 to 1.0 |\n| **Reranker** | LLM 0-10 rating | `score / 10` | 0.0 to 1.0 |\n\n### Fusion Strategy\n\nThe `query` command uses **Reciprocal Rank Fusion (RRF)** with position-aware blending:\n\n1. **Query Expansion**: Original query (×2 for weighting) + 1 LLM variation\n2. **Parallel Retrieval**: Each query searches both FTS and vector indexes\n3. **RRF Fusion**: Combine all result lists using `score = Σ(1/(k+rank+1))` where k=60\n4. **Top-Rank Bonus**: Documents ranking #1 in any list get +0.05, #2-3 get +0.02\n5. **Top-K Selection**: Take top 30 candidates for reranking\n6. **Re-ranking**: LLM scores each document (yes/no with logprobs confidence)\n7. **Position-Aware Blending**:\n   - RRF rank 1-3: 75% retrieval, 25% reranker (preserves exact matches)\n   - RRF rank 4-10: 60% retrieval, 40% reranker\n   - RRF rank 11+: 40% retrieval, 60% reranker (trust reranker more)\n\n**Why this approach**: Pure RRF can dilute exact matches when expanded queries don't match. The top-rank bonus preserves documents that score #1 for the original query. Position-aware blending prevents the reranker from destroying high-confidence retrieval results.\n\n### Score Interpretation\n\n| Score | Meaning |\n|-------|---------|\n| 0.8 - 1.0 | Highly relevant |\n| 0.5 - 0.8 | Moderately relevant |\n| 0.2 - 0.5 | Somewhat relevant |\n| 0.0 - 0.2 | Low relevance |\n\n## Requirements\n\n### System Requirements\n\n- **Node.js** >= 22\n- **Bun** >= 1.0.0\n- **macOS**: Homebrew SQLite (for extension support)\n  ```sh\n  brew install sqlite\n  ```\n\n### GGUF Models (via node-llama-cpp)\n\nQMD uses three local GGUF models (auto-downloaded on first use):\n\n| Model | Purpose | Size |\n|-------|---------|------|\n| `embeddinggemma-300M-Q8_0` | Vector embeddings (default) | ~300MB |\n| `qwen3-reranker-0.6b-q8_0` | Re-ranking | ~640MB |\n| `qmd-query-expansion-1.7B-q4_k_m` | Query expansion (fine-tuned) | ~1.1GB |\n\nModels are downloaded from HuggingFace and cached in `~/.cache/qmd/models/`.\n\n### Custom Embedding Model\n\nOverride the default embedding model via the `QMD_EMBED_MODEL` environment variable.\nThis is useful for multilingual corpora (e.g. Chinese, Japanese, Korean) where\n`embeddinggemma-300M` has limited coverage.\n\n```sh\n# Use Qwen3-Embedding-0.6B for better multilingual (CJK) support\nexport QMD_EMBED_MODEL=\"hf:Qwen/Qwen3-Embedding-0.6B-GGUF/Qwen3-Embedding-0.6B-Q8_0.gguf\"\n\n# After changing the model, re-embed all collections:\nqmd embed -f\n```\n\nSupported model families:\n- **embeddinggemma** (default) — English-optimized, small footprint\n- **Qwen3-Embedding** — Multilingual (119 languages including CJK), MTEB top-ranked\n\n> **Note:** When switching embedding models, you must re-index with `qmd embed -f`\n> since vectors are not cross-compatible between models. The prompt format is\n> automatically adjusted for each model family.\n\n## Installation\n\n```sh\nnpm install -g @tobilu/qmd\n# or\nbun install -g @tobilu/qmd\n```\n\n### Development\n\n```sh\ngit clone https://github.com/tobi/qmd\ncd qmd\nnpm install\nnpm link\n```\n\n## Usage\n\n### Collection Management\n\n```sh\n# Create a collection from current directory\nqmd collection add . --name myproject\n\n# Create a collection with explicit path and custom glob mask\nqmd collection add ~/Documents/notes --name notes --mask \"**/*.md\"\n\n# Comma-separated masks are a union (brace form `{a,b}` also works)\nqmd collection add ~/notes --name notes --mask \"sources/**/*.md,CO - *.md\"\n\n# List all collections\nqmd collection list\n\n# Remove a collection\nqmd collection remove myproject\n\n# Rename a collection\nqmd collection rename myproject my-project\n\n# List files in a collection\nqmd ls notes\nqmd ls notes/subfolder\n\n# Show collection details (path, glob mask, include status, context count)\nqmd collection show notes\n\n# Include or exclude a collection from default (unscoped) queries\nqmd collection include notes\nqmd collection exclude notes\n\n# Run a command before every `qmd update` (e.g. git pull); empty arg clears it\nqmd collection update-cmd notes 'git pull --rebase'\nqmd collection update-cmd notes\n```\n\n### Generate Vector Embeddings\n\n```sh\n# Embed all indexed documents (900 tokens/chunk, 15% overlap)\nqmd embed\n\n# Force re-embed everything\nqmd embed -f\n\n# Enable AST-aware chunking for code files (TS, JS, Python, Go, Rust)\nqmd embed --chunk-strategy auto\n\n# Also works with query for consistent chunk selection\nqmd query \"auth flow\" --chunk-strategy auto\n\n# Memory control for large corpora / constrained systems\nqmd embed --max-docs-per-batch 50   # cap docs per embedding batch\nqmd embed --max-batch-mb 64         # cap batch size in MB\n```\n\n**AST-aware chunking** (`--chunk-strategy auto`) uses tree-sitter to chunk code\nfiles at function, class, and import boundaries instead of arbitrary text\npositions. This produces higher-quality chunks and better search results for\ncodebases. Markdown and other file types always use regex-based chunking\nregardless of strategy.\n\nThe default is `regex` (existing behavior). Use `--chunk-strategy auto` to\nopt in. Run `qmd status` to verify which grammars are available.\n\n> **Note:** Tree-sitter grammars are optional dependencies. If they are not\n> installed, `--chunk-strategy auto` falls back to regex-only chunking\n> automatically. Tested on both Node.js and Bun.\n\n### Context Management\n\nContext adds descriptive metadata to collections and paths, helping search understand your content.\n\n```sh\n# Add context to a collection (using qmd:// virtual paths)\nqmd context add qmd://notes \"Personal notes and ideas\"\nqmd context add qmd://docs/api \"API documentation\"\n\n# Add context from within a collection directory\ncd ~/notes && qmd context add \"Personal notes and ideas\"\ncd ~/notes/work && qmd context add \"Work-related notes\"\n\n# Add global context (applies to all collections)\nqmd context add / \"Knowledge base for my projects\"\n\n# List all contexts\nqmd context list\n\n# Remove context\nqmd context rm qmd://notes/old\n```\n\n### Configuring `index.yml`\n\nThe `collection` and `context` commands above all read and write a single YAML\nconfig file — you can also edit it directly. Everything QMD knows about your\ncollections (paths, masks, exclusions, per-collection update hooks, contexts, and\noptional model overrides) lives here. A fully-commented starter template ships as\n[`example-index.yml`](example-index.yml) in this repo.\n\n**Location:** `~/.config/qmd/index.yml` by default. The directory honors\n`XDG_CONFIG_HOME` (→ `$XDG_CONFIG_HOME/qmd/index.yml`) and `QMD_CONFIG_DIR`. A\nnamed index uses `{name}.yml` — `qmd --index work …` reads/writes `work.yml`.\nA **project-local** index created with `qmd init` lives at `.qmd/index.yml`\n(`.qmd/index.yaml` is also accepted) alongside a project-local `index.sqlite`,\nso config and index stay inside the project instead of `~/.config` / `~/.cache`.\n\n```yaml\n# ~/.config/qmd/index.yml\n\n# Context applied to every collection (system-message style). Optional.\nglobal_context: \"Knowledge base for my projects\"\n\n# Terminal hyperlink template for search results. Optional.\n# Overridden by the QMD_EDITOR_URI env var. See \"Editor Links\" below.\neditor_uri: \"vscode://file{path}:{line}:{col}\"\n\n# Override the default GGUF models per role. Optional — omit to use the\n# built-in defaults. `qmd init` writes this block pre-filled with the\n# resolved defaults. See \"Model Configuration\" for the default URIs.\nmodels:\n  embed: \"hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf\"\n  rerank: \"hf:ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF/qwen3-reranker-0.6b-q8_0.gguf\"\n  generate: \"hf:tobil/qmd-query-expansion-1.7B-gguf/qmd-query-expansion-1.7B-q4_k_m.gguf\"\n\n# One entry per collection. The key is the collection name.\ncollections:\n  notes:\n    path: /Users/me/notes        # absolute path to index (required)\n    pattern: \"**/*.md\"           # glob mask (default: **/*.md)\n    ignore:                      # glob patterns to exclude from indexing\n      - \"Archive/**\"\n      - \"**/drafts/**\"\n    update: \"git pull --rebase\"  # bash command run before each `qmd update`\n    includeByDefault: true       # include in unscoped queries (default: true)\n    context:                     # path prefix → description; longest match wins\n      \"/\": \"Personal notes and ideas\"\n      \"/work\": \"Work-related notes\"\n```\n\n| Key | Scope | Purpose |\n|-----|-------|---------|\n| `global_context` | top-level | Context prepended for every collection. Set via `qmd context add /`. |\n| `editor_uri` (alias `editor_uri_template`) | top-level | Hyperlink template for clickable result paths; `QMD_EDITOR_URI` overrides. |\n| `models.embed` / `.rerank` / `.generate` | top-level | HuggingFace GGUF URIs (`hf:<user>/<repo>/<file>`) overriding the built-in defaults per role. |\n| `collections.<name>.path` | per-collection | Absolute directory to index. |\n| `collections.<name>.pattern` | per-collection | Glob mask. Set via `qmd collection add --mask`. Default `**/*.md`. Comma-separated lists and brace groups (`{a,b}`) are a union of patterns. |\n| `collections.<name>.ignore` | per-collection | Glob patterns excluded from indexing — useful to stop nested collections double-indexing. **YAML-only — no CLI command sets this.** Additive with QMD's built-in exclusions (`node_modules`, `.git`, `.cache`, `vendor`, `dist`, `build`), which you cannot un-ignore. |\n| `collections.<name>.update` | per-collection | Bash command run before `qmd update` re-indexes this collection. Set via `qmd collection update-cmd`. |\n| `collections.<name>.includeByDefault` | per-collection | Whether unscoped queries search it. Toggle with `qmd collection include`/`exclude`. Default `true`. |\n| `collections.<name>.context` | per-collection | Path-prefix → description map; the most specific (longest) matching prefix wins. Set via `qmd context add`. |\n\n> **Note:** Editing `index.yml` changes which directories and models QMD *uses*,\n> but does not re-index on its own. Run `qmd update` after changing `path`,\n> `pattern`, or `ignore`, and `qmd embed` after changing `models.embed`.\n\n#### Automatic update commands\n\nA collection's `update` field is QMD's built-in refresh hook: when you run\n`qmd update`, each collection's `update` command runs **first**, then the\ncollection is re-indexed. This keeps a collection in sync with an upstream source\n(a git remote, a sync script) without wrapping `qmd` yourself.\n\n```yaml\ncollections:\n  wiki:\n    path: ~/reference/wiki\n    update: \"git pull --ff-only\"\n```\n\n    $ qmd update\n    [1/3] wiki (**/*.md)\n        Running update command: git pull --ff-only\n        Already up to date.\n    Collection: ~/reference/wiki (**/*.md)\n    Indexed: 0 new, 2 updated, 340 unchanged, 0 removed\n\nThe command runs via `bash -c` in the collection's own directory (its `path`), not\nyour current working directory. If it exits non-zero, `qmd update` prints the\nfailure and **aborts the entire run** — collections after the failing one are not\nre-indexed. Set or clear it from the CLI instead of editing YAML by hand:\n\n```sh\nqmd collection update-cmd wiki 'git pull --ff-only'   # set\nqmd collection update-cmd wiki                         # clear\n```\n\n##### Checked-in `.qmd` config is not trusted by default\n\nA project-local `.qmd/index.yml` travels with a `git clone`, and QMD adopts it\nautomatically for any command run inside the tree. Three fields in that file can\nreach outside the project, and QMD will not use them unattended:\n\n- `update` commands — somebody else's shell script, run by `qmd update`\n- `collections.*.path` pointing **outside** the project directory\n- `models.embed` / `models.rerank` / `models.generate` other than the built-in\n  defaults (any `hf:` repo or local GGUF path)\n\nIn-project collection paths (for example `./docs`) still index. On a terminal\n`qmd update` (and `qmd embed` / `qmd pull` / `qmd query`) lists the gated\nfields and asks. Approving records the approval in `~/.config/qmd/trusted.json`.\nWith no terminal to ask — agents, CI, MCP — those fields are **skipped** and\nin-project indexing continues.\n\nApprovals cover the exact gated set you saw. Editing a command, pointing a\ncollection outside the project, or changing a custom model URI asks again.\n\n```sh\nqmd trust           # review and approve this project's gated fields\nqmd trust list      # show every approved project config\nqmd trust revoke    # drop the approval for this project\n```\n\nSet `QMD_TRUST_LOCAL_CONFIG=1` (or `QMD_TRUST_UPDATE_HOOKS=1`) for CI that\nshould allow them unattended. Your own `~/.config/qmd/*.yml` — including\nanything `qmd collection update-cmd` or `qmd collection add` writes — is\nnever gated.\n\n### Search Commands\n\n```\n┌──────────────────────────────────────────────────────────────────┐\n│                        Search Modes                              │\n├──────────┬───────────────────────────────────────────────────────┤\n│ search   │ BM25 full-text search only                           │\n│ vsearch  │ Vector semantic search only                          │\n│ query    │ Hybrid: FTS + Vector + Query Expansion + Re-ranking  │\n└──────────┴───────────────────────────────────────────────────────┘\n```\n\n```sh\n# Full-text search (fast, keyword-based)\nqmd search \"authentication flow\"\n\n# Vector search (semantic similarity)\nqmd vsearch \"how to login\"\n\n# Hybrid search with re-ranking (best quality)\nqmd query \"user authentication\"\n```\n\nTwo aliases exist for the semantic/hybrid modes: `vector-search` (→ `vsearch`)\nand `deep-search` (→ `query`).\n\n### Options\n\n```sh\n# Search options\n-n <num>           # Number of results (default: 5, or 20 for --files/--json)\n-c, --collection   # Restrict search to a specific collection\n--all              # Return all matches (use with --min-score to filter)\n--min-score <num>  # Minimum score threshold (default: 0)\n--full             # Show full document content\n--line-numbers     # Add line numbers to output\n--explain          # Include retrieval score traces (query, JSON/CLI output)\n--index <name>     # Use named index\n--intent \"<text>\"  # Disambiguation context (e.g. \"web page load times\")\n--no-rerank        # Skip LLM reranking (RRF scores only; faster on CPU)\n-C, --candidate-limit <n>  # Max candidates to rerank (default: 40)\n--full-path        # Emit on-disk filesystem paths instead of qmd:// URIs\n                   # (a result whose file has moved or been deleted since\n                   #  indexing keeps its qmd:// URI + docid, and a notice is\n                   #  printed to stderr — run `qmd update` to refresh)\n\n# Output formats (for search and multi-get)\n--format <kind>    # cli (default) | json | csv | md | xml | files\n                   # (--json, --csv, --md, --xml, --files are legacy aliases)\n\n# Get options\nqmd get <file>[:from[:count]]  # Get document; optional start line and count\n-l <num>                       # Maximum lines to return\n--from <num>                   # Start line (overrides the :from suffix)\n--no-line-numbers              # Disable line numbering (on by default)\n\n# Multi-get options\n-l <num>           # Maximum lines per file\n--max-bytes <num>  # Skip files larger than N bytes (default: 64KB)\n```\n\n### Collection Filtering\n\nThe `-c`/`--collection` flag filters results by collection **name** (as shown by\n`qmd collection list`). Collections are a global registry — you can search any\ncollection from any directory:\n\n```sh\nqmd search \"auth\" -c notes           # single collection\nqmd search \"auth\" -c notes -c docs   # multiple collections (OR)\n```\n\nWith no `-c` flag, all default-included collections are searched. Collections\nmarked excluded (`qmd collection exclude <name>`) are skipped unless named\nexplicitly with `-c`.\n\n> **Note:** With multiple `-c` flags, results come from a global top-K pool and are\n> then filtered. If one collection dominates the rankings, matches from smaller\n> collections may not appear at the default limit — raise `-n` or use `--all`.\n\n### Output Format\n\nDefault output is colorized CLI format (respects `NO_COLOR` env).\n\nWhen stdout is a TTY, result paths are emitted as clickable terminal hyperlinks (OSC 8). Clicking a path opens the file in your editor using an editor URI template.\n\nWhen stdout is not a TTY (for example piped to another command or redirected to a file), QMD emits plain text paths with no escape sequences.\n\nTTY example:\n\n```\ndocs/guide.md:42 #a1b2c3\nTitle: Software Craftsmanship\nContext: Work documentation\nScore: 93%\n\nThis section covers the **craftsmanship** of building\nquality software with attention to detail.\nSee also: engineering principles\n\n\nnotes/meeting.md:15 #d4e5f6\nTitle: Q4 Planning\nContext: Personal notes and ideas\nScore: 67%\n\nDiscussion about code quality and craftsmanship\nin the development process.\n```\n\nConfigure the editor link target with `QMD_EDITOR_URI` (or `editor_uri` in config):\n\n```sh\n# VS Code (default)\nexport QMD_EDITOR_URI=\"vscode://file/{path}:{line}:{col}\"\n\n# Cursor\nexport QMD_EDITOR_URI=\"cursor://file/{path}:{line}:{col}\"\n\n# Zed\nexport QMD_EDITOR_URI=\"zed://file/{path}:{line}:{col}\"\n\n# Sublime Text\nexport QMD_EDITOR_URI=\"subl://open?url=file://{path}&line={line}\"\n```\n\nTemplate placeholders:\n- `{path}` absolute filesystem path (URI-encoded)\n- `{line}` 1-based line number\n- `{col}` or `{column}` 1-based column number\n\n- **Path**: Collection-relative path (e.g., `docs/guide.md`)\n- **Docid**: Short hash identifier (e.g., `#a1b2c3`) - use with `qmd get #a1b2c3`\n- **Title**: Extracted from document (first heading or filename)\n- **Context**: Path context if configured via `qmd context add`\n- **Score**: Color-coded (green >70%, yellow >40%, dim otherwise)\n- **Snippet**: Context around match with query terms highlighted\n\n### Examples\n\n```sh\n# Get 10 results with minimum score 0.3\nqmd query -n 10 --min-score 0.3 \"API design patterns\"\n\n# Output as markdown for LLM context\nqmd search --md --full \"error handling\"\n\n# JSON output for scripting\nqmd query --json \"quarterly reports\"\n\n# Inspect how each result was scored (RRF + rerank blend)\nqmd query --json --explain \"quarterly reports\"\n\n# Use separate index for different knowledge base\nqmd --index work search \"quarterly reports\"\n```\n\nThe `--explain` flag attaches a score breakdown to each result: the FTS/vector\nbackend scores plus the RRF fusion math (rank, weight, top-rank bonus) and every\nsub-query's contribution. Abbreviated:\n\n```json\n{\n  \"docid\": \"#6c90f0\",\n  \"score\": 0.89,\n  \"file\": \"qmd://qmd/README.md\",\n  \"explain\": {\n    \"ftsScores\": [0.892, 0.907],\n    \"vectorScores\": [0.540, 0.484],\n    \"rrf\": {\n      \"rank\": 1,\n      \"weight\": 0.75,\n      \"baseScore\": 0.123,\n      \"topRankBonus\": 0.05,\n      \"totalScore\": 0.173,\n      \"contributions\": [\n        { \"source\": \"fts\", \"queryType\": \"original\", \"query\": \"reranking\",\n          \"rank\": 1, \"weight\": 2, \"backendScore\": 0.892, \"rrfContribution\": 0.0328 }\n      ]\n    }\n  }\n}\n```\n\n### Index Maintenance\n\n```sh\n# Show index status and collections with contexts\nqmd status\n\n# Re-index all collections. If a collection has a configured update command\n# (e.g. `git pull`), it runs first — set one with `qmd collection update-cmd`.\nqmd update\n\n# Diagnose the install (runtime, sqlite-vec, embedding fingerprints, GPU probe)\nqmd doctor\n\n# Initialize a project-local index in the current directory\nqmd init\n\n# Get document by filepath (with fuzzy matching suggestions)\nqmd get notes/meeting.md\n\n# Get document by docid (from search results)\nqmd get \"#abc123\"\n\n# Get document starting at line 50, max 100 lines\nqmd get notes/meeting.md:50 -l 100\n\n# Read 40 lines starting at line 120 via the :from:count suffix (works with docids)\nqmd get notes/meeting.md:120:40\nqmd get \"#abc123:120:40\"\n\n# get / multi-get are line-numbered by default; disable with --no-line-numbers\nqmd get notes/meeting.md --no-line-numbers\n\n# Get multiple documents by glob pattern\nqmd multi-get \"journals/2025-05*.md\"\n\n# Get multiple documents by comma-separated list (supports docids)\nqmd multi-get \"doc1.md, doc2.md, #abc123\"\n\n# Limit multi-get to files under 20KB\nqmd multi-get \"docs/*.md\" --max-bytes 20480\n\n# Output multi-get as JSON for agent processing\nqmd multi-get \"docs/*.md\" --json\n\n# Clean up cache and orphaned data\nqmd cleanup\n```\n\n### Benchmarking\n\nMeasure search quality across all four backends with `qmd bench` and a fixture file\nof queries with known-relevant documents.\n\n**From a git checkout**, an example fixture and its test corpus ship in the repo:\n\n```sh\n# One-time setup (indexes the repo's test corpus into its own collection)\nqmd collection add test/eval-docs --name eval-docs\nqmd embed -c eval-docs\n\n# Run the benchmark (table output)\nqmd bench src/bench/fixtures/example.json\n\n# JSON output for programmatic analysis\nqmd bench src/bench/fixtures/example.json --json\n```\n\n> The example fixture (`src/bench/fixtures/example.json`) and its test corpus\n> (`test/eval-docs/`) exist only in a git checkout — they are **not** part of the\n> published npm package. If you installed via `npm`/`npx`, write your own fixture\n> (see below) against a collection you have already indexed:\n>\n> ```sh\n> qmd bench my-fixture.json -c my-collection\n> ```\n\nEach query runs against four backends, reporting precision@k, recall, MRR, and F1:\n\n| Backend | What it tests | LLM required |\n|---------|---------------|--------------|\n| `bm25` | Keyword search only (FTS5) | No |\n| `vector` | Semantic similarity only | Embedding model |\n| `hybrid` | BM25 + vector fusion (no reranking) | Embedding model |\n| `full` | Full pipeline with LLM reranking | All three models |\n\n**Score interpretation:** `1.00` = perfect (all expected docs in top results),\n`0.00` = complete miss. The example fixture typically shows bm25 ~0.50, vector\n~0.70, and hybrid/full ~1.00 — a concrete demonstration of why hybrid search beats\neither backend alone.\n\n**Custom fixtures** are JSON:\n\n```json\n{\n  \"description\": \"My benchmark\",\n  \"version\": 1,\n  \"collection\": \"my-collection\",\n  \"queries\": [\n    {\n      \"id\": \"find-auth\",\n      \"query\": \"authentication flow\",\n      \"type\": \"semantic\",\n      \"expected_files\": [\"docs/auth-design.md\"],\n      \"expected_in_top_k\": 3\n    }\n  ]\n}\n```\n\n`expected_files` are collection-relative paths as shown by `qmd ls`. The `type`\nfield (`exact`, `semantic`, `topical`, `cross-domain`, `alias`) labels queries for\ngrouping — it does not change search behavior.\n\n> **Heads-up:** if the fixture's collection isn't indexed, bench currently runs to\n> completion and reports all zeros with no warning. Verify setup with\n> `qmd ls <collection>` first.\n\n## Data Storage\n\nIndex stored in: `~/.cache/qmd/index.sqlite`\n\n### Schema\n\n```sql\ncollections     -- Indexed directories with name and glob patterns\npath_contexts   -- Context descriptions by virtual path (qmd://...)\ndocuments       -- Markdown content with metadata and docid (6-char hash)\ndocuments_fts   -- FTS5 full-text index\ncontent_vectors -- Embedding chunks (hash, seq, pos, 900 tokens each)\nvectors_vec     -- sqlite-vec vector index (hash_seq key)\nllm_cache       -- Cached LLM responses (query expansion, rerank scores)\n```\n\n## Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `XDG_CACHE_HOME` | `~/.cache` | Cache directory location |\n| `XDG_CONFIG_HOME` | `~/.config` | Config directory location (where `index.yml` lives) |\n| `QMD_CONFIG_DIR` | unset | Override the config directory outright (takes precedence over `XDG_CONFIG_HOME`) |\n| `QMD_LLAMA_GPU` | `auto` | Force llama.cpp GPU backend (`metal`, `vulkan`, `cuda`) or disable GPU with `false` |\n| `QMD_FORCE_CPU` | unset | Set to `1`/`true` to force CPU mode before any CUDA/Vulkan/Metal probing. Equivalent CLI flag: `--no-gpu`. |\n| `QMD_EMBED_PARALLELISM` | automatic | Override embedding/reranking context parallelism (1-8). Windows CUDA defaults to `1` because parallel CUDA contexts can crash with `ggml-cuda.cu:98`; use Vulkan or raise this only if your driver is stable. |\n\n## How It Works\n\n### Indexing Flow\n\n```\nCollection ──► Glob Pattern ──► Markdown Files ──► Parse Title ──► Hash Content\n    │                                                   │              │\n    │                                                   │              ▼\n    │                                                   │         Generate docid\n    │                                                   │         (6-char hash)\n    │                                                   │              │\n    └──────────────────────────────────────────────────►└──► Store in SQLite\n                                                                       │\n                                                                       ▼\n                                                                  FTS5 Index\n```\n\n### Embedding Flow\n\nDocuments are chunked into ~900-token pieces with 15% overlap using smart boundary detection:\n\n```\nDocument ──► Smart Chunk (~900 tokens) ──► Format each chunk ──► node-llama-cpp ──► Store Vectors\n                │                           \"title | text\"        embedBatch()\n                │\n                └─► Chunks stored with:\n                    - hash: document hash\n                    - seq: chunk sequence (0, 1, 2...)\n                    - pos: character position in original\n```\n\n### Smart Chunking\n\nInstead of cutting at hard token boundaries, QMD uses a scoring algorithm to find natural markdown break points. This keeps semantic units (sections, paragraphs, code blocks) together.\n\n**Break Point Scores:**\n\n| Pattern | Score | Description |\n|---------|-------|-------------|\n| `# Heading` | 100 | H1 - major section |\n| `## Heading` | 90 | H2 - subsection |\n| `### Heading` | 80 | H3 |\n| `#### Heading` | 70 | H4 |\n| `##### Heading` | 60 | H5 |\n| `###### Heading` | 50 | H6 |\n| ` ``` ` | 80 | Code block boundary |\n| `---` / `***` | 60 | Horizontal rule |\n| Blank line | 20 | Paragraph boundary |\n| `- item` / `1. item` | 5 | List item |\n| Line break | 1 | Minimal break |\n\n**Algorithm:**\n\n1. Scan document for all break points with scores\n2. When approaching the 900-token target, search a 200-token window before the cutoff\n3. Score each break point: `finalScore = baseScore × (1 - (distance/window)² × 0.7)`\n4. Cut at the highest-scoring break point\n\nThe squared distance decay means a heading 200 tokens back (score ~30) still beats a simple line break at the target (score 1), but a closer heading wins over a distant one.\n\n**Code Fence Protection:** Break points inside code blocks are ignored—code stays together. If a code block exceeds the chunk size, it's kept whole when possible.\n\n**AST-Aware Chunking (Code Files):**\n\nFor supported code files, QMD also parses the source with [tree-sitter](https://tree-sitter.github.io/) and adds AST-derived break points that are merged with the regex scores above:\n\n| AST Node | Score | Languages |\n|----------|-------|-----------|\n| Class / interface / struct / impl / trait | 100 | All |\n| Function / method | 90 | All |\n| Type alias / enum | 80 | All |\n| Import / use declaration | 60 | All |\n\nSupported for `.ts`, `.tsx`, `.js`, `.jsx`, `.py`, `.go`, and `.rs` files. Enable with `--chunk-strategy auto`. Markdown and other file types always use regex chunking.\n\n### Query Flow (Hybrid)\n\n```\nQuery ──► LLM Expansion ──► [Original, Variant 1, Variant 2]\n                │\n      ┌─────────┴─────────┐\n      ▼                   ▼\n   For each query:     FTS (BM25)\n      │                   │\n      ▼                   ▼\n   Vector Search      Ranked List\n      │\n      ▼\n   Ranked List\n      │\n      └─────────┬─────────┘\n                ▼\n         RRF Fusion (k=60)\n         Original query ×2 weight\n         Top-rank bonus: +0.05/#1, +0.02/#2-3\n                │\n                ▼\n         Top 30 candidates\n                │\n                ▼\n         LLM Re-ranking\n         (yes/no + logprob confidence)\n                │\n                ▼\n         Position-Aware Blend\n         Rank 1-3:  75% RRF / 25% reranker\n         Rank 4-10: 60% RRF / 40% reranker\n         Rank 11+:  40% RRF / 60% reranker\n                │\n                ▼\n         Final Results\n```\n\n## Model Configuration\n\nThe default models are defined in `src/llm.ts` as HuggingFace URIs:\n\n```typescript\nconst DEFAULT_EMBED_MODEL = \"hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf\";\nconst DEFAULT_RERANK_MODEL = \"hf:ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF/qwen3-reranker-0.6b-q8_0.gguf\";\nconst DEFAULT_GENERATE_MODEL = \"hf:tobil/qmd-query-expansion-1.7B-gguf/qmd-query-expansion-1.7B-q4_k_m.gguf\";\n```\n\nOverride them per-role without touching source via the `models:` block in\n`index.yml` (see [Configuring `index.yml`](#configuring-indexyml)) or the\n`QMD_EMBED_MODEL` env var. Re-run `qmd embed` after changing the embedding model.\n\n### EmbeddingGemma Prompt Format\n\n```\n// For queries\n\"task: search result | query: {query}\"\n\n// For documents\n\"title: {title} | text: {content}\"\n```\n\n### Qwen3-Reranker\n\nUses node-llama-cpp's `createRankingContext()` and `rankAndSort()` API for cross-encoder reranking. Returns documents sorted by relevance score (0.0 - 1.0).\n\n### Qwen3 (Query Expansion)\n\nUsed for generating query variations via `LlamaChatSession`.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}