@wei840222/qmd 2026.8.28 → 2026.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +0 -1
- package/dist/cli/build-info.json +2 -2
- package/dist/cli/qmd.js +11 -9
- package/dist/db.d.ts +16 -29
- package/dist/db.js +63 -40
- package/dist/store.js +13 -5
- package/package.json +2 -4
- package/skills/qmd/SKILL.md +0 -2
- package/skills/qmd/references/query-syntax.md +1 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Replace `better-sqlite3` with Node.js built-in `node:sqlite`, preventing SQLite runtime symbol collisions when QMD is embedded alongside other `node:sqlite` consumers. The minimum supported Node.js version is now 22.16.0.
|
|
8
|
+
|
|
5
9
|
### Added
|
|
6
10
|
|
|
7
11
|
- **Disable HyDE Expansion Control**: Added `--no-hyde` CLI option for `qmd query` and `qmd vsearch`, `includeHyde` parameter to SDK (`store.search`, `store.expandQuery`) and MCP `query` tool, allowing users to disable generating hypothetical document embeddings during query expansion.
|
package/README.md
CHANGED
|
@@ -982,7 +982,6 @@ and `deep-search` (→ `query`).
|
|
|
982
982
|
--index <name> # Use named index
|
|
983
983
|
--intent "<text>" # Legacy CLI alias for rerank context (e.g. "web page load times")
|
|
984
984
|
--no-rerank # Skip LLM reranking (RRF scores only; faster on CPU)
|
|
985
|
-
--no-hyde # Disable HyDE in query expansion (only lex and vec expansions)
|
|
986
985
|
-C, --candidate-limit <n> # Max candidates to rerank (default: 40)
|
|
987
986
|
--full-path # Emit on-disk filesystem paths instead of qmd:// URIs
|
|
988
987
|
# (a result whose file has moved or been deleted since
|
package/dist/cli/build-info.json
CHANGED
package/dist/cli/qmd.js
CHANGED
|
@@ -137,12 +137,16 @@ function getStore() {
|
|
|
137
137
|
}
|
|
138
138
|
return store;
|
|
139
139
|
}
|
|
140
|
-
function getDoctorStore() {
|
|
140
|
+
function getDoctorStore(options = {}) {
|
|
141
141
|
if (!store) {
|
|
142
142
|
const dbPath = getDbPath();
|
|
143
|
+
const shouldReconcile = existsSync(dbPath) && options.reconcileConfig !== undefined;
|
|
143
144
|
store = existsSync(dbPath)
|
|
144
|
-
? createStore(dbPath, { readOnly:
|
|
145
|
+
? createStore(dbPath, { readOnly: !shouldReconcile })
|
|
145
146
|
: createStore(":memory:");
|
|
147
|
+
if (shouldReconcile) {
|
|
148
|
+
syncConfigToDb(store.db, options.reconcileConfig);
|
|
149
|
+
}
|
|
146
150
|
}
|
|
147
151
|
return store;
|
|
148
152
|
}
|
|
@@ -3916,10 +3920,6 @@ async function runDoctorDeviceChecks(nextSteps) {
|
|
|
3916
3920
|
}
|
|
3917
3921
|
}
|
|
3918
3922
|
async function showDoctor() {
|
|
3919
|
-
const storeInstance = getDoctorStore();
|
|
3920
|
-
const db = storeInstance.db;
|
|
3921
|
-
const pkg = readPackageJson();
|
|
3922
|
-
const activeModels = resolveModelsForCli();
|
|
3923
3923
|
let doctorConfig;
|
|
3924
3924
|
try {
|
|
3925
3925
|
doctorConfig = loadConfig();
|
|
@@ -3928,6 +3928,9 @@ async function showDoctor() {
|
|
|
3928
3928
|
// The dedicated index-config check below reports parse errors. Keep the
|
|
3929
3929
|
// remaining diagnostics available by falling back to DB/default config.
|
|
3930
3930
|
}
|
|
3931
|
+
const storeInstance = getDoctorStore({ reconcileConfig: doctorConfig });
|
|
3932
|
+
const db = storeInstance.db;
|
|
3933
|
+
const activeModels = resolveModelsForCli();
|
|
3931
3934
|
const doctorEmbedding = resolveEmbeddingConfig({
|
|
3932
3935
|
config: doctorConfig,
|
|
3933
3936
|
dbConfig: readCanonicalEmbeddingConfig(db),
|
|
@@ -3938,7 +3941,7 @@ async function showDoctor() {
|
|
|
3938
3941
|
const nextSteps = [];
|
|
3939
3942
|
console.log(`${c.bold}QMD Doctor${c.reset}\n`);
|
|
3940
3943
|
console.log(`Index: ${getDbPath()}`);
|
|
3941
|
-
console.log(`Runtime:
|
|
3944
|
+
console.log(`Runtime: node:sqlite`);
|
|
3942
3945
|
try {
|
|
3943
3946
|
const row = db.prepare(`SELECT sqlite_version() AS version`).get();
|
|
3944
3947
|
doctorCheck("SQLite runtime", true, row.version);
|
|
@@ -3946,8 +3949,7 @@ async function showDoctor() {
|
|
|
3946
3949
|
catch (error) {
|
|
3947
3950
|
doctorCheck("SQLite runtime", false, error instanceof Error ? error.message : String(error));
|
|
3948
3951
|
}
|
|
3949
|
-
|
|
3950
|
-
doctorCheck("better-sqlite3 package", true, String(betterSqliteVersion));
|
|
3952
|
+
doctorCheck("node:sqlite", true, process.versions.node);
|
|
3951
3953
|
try {
|
|
3952
3954
|
loadSqliteVec(db);
|
|
3953
3955
|
const row = db.prepare(`SELECT vec_version() AS version`).get();
|
package/dist/db.d.ts
CHANGED
|
@@ -1,39 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* db.ts - SQLite database connection and extension management
|
|
3
3
|
*
|
|
4
|
-
* Provides
|
|
5
|
-
* and sqlite-vec.
|
|
4
|
+
* Provides a synchronous node:sqlite connection with QMD's transaction helper
|
|
5
|
+
* and sqlite-vec extension loading.
|
|
6
6
|
*/
|
|
7
|
-
import
|
|
7
|
+
import { DatabaseSync, type StatementSync } from "node:sqlite";
|
|
8
8
|
export type SQLiteValue = string | number | bigint | Buffer | Uint8Array | Float32Array | null;
|
|
9
9
|
export type SQLiteParams = readonly SQLiteValue[];
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
* Default 120_000 ms outlasts the worst-case batch commit on a multi-GB
|
|
23
|
-
* index. Override with `QMD_SQLITE_BUSY_TIMEOUT` (value in milliseconds; `0`
|
|
24
|
-
* restores the upstream fail-fast behaviour).
|
|
25
|
-
*/
|
|
10
|
+
export type Transaction<TArgs extends unknown[], TResult> = ((...args: TArgs) => TResult) & {
|
|
11
|
+
deferred: (...args: TArgs) => TResult;
|
|
12
|
+
immediate: (...args: TArgs) => TResult;
|
|
13
|
+
exclusive: (...args: TArgs) => TResult;
|
|
14
|
+
};
|
|
15
|
+
/** Synchronous SQLite connection with QMD-compatible transactions. */
|
|
16
|
+
export declare class Database extends DatabaseSync {
|
|
17
|
+
transaction<TArgs extends unknown[], TResult>(operation: (...args: TArgs) => TResult): Transaction<TArgs, TResult>;
|
|
18
|
+
}
|
|
19
|
+
/** Statement type used throughout QMD. */
|
|
20
|
+
export type Statement<T extends SQLiteParams = SQLiteParams> = StatementSync;
|
|
21
|
+
/** Open a writable QMD database using Node's built-in SQLite runtime. */
|
|
26
22
|
export declare function openDatabase(path: string): Database;
|
|
27
23
|
/** Open an existing database without changing journal mode, schema, or user data. */
|
|
28
24
|
export declare function openReadOnlyDatabase(path: string): Database;
|
|
29
|
-
/**
|
|
30
|
-
* Database and Statement types used throughout QMD.
|
|
31
|
-
*/
|
|
32
|
-
export type Database = BetterSqlite3.Database;
|
|
33
|
-
export type Statement<T extends SQLiteParams = SQLiteParams> = BetterSqlite3.Statement<T>;
|
|
34
|
-
/**
|
|
35
|
-
* Load the sqlite-vec extension into a database.
|
|
36
|
-
*
|
|
37
|
-
* Throws with fix instructions when the extension is unavailable.
|
|
38
|
-
*/
|
|
25
|
+
/** Load the sqlite-vec extension into a database. */
|
|
39
26
|
export declare function loadSqliteVec(db: Database): void;
|
package/dist/db.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* db.ts - SQLite database connection and extension management
|
|
3
3
|
*
|
|
4
|
-
* Provides
|
|
5
|
-
* and sqlite-vec.
|
|
4
|
+
* Provides a synchronous node:sqlite connection with QMD's transaction helper
|
|
5
|
+
* and sqlite-vec extension loading.
|
|
6
6
|
*/
|
|
7
|
-
import
|
|
7
|
+
import { DatabaseSync } from "node:sqlite";
|
|
8
8
|
import * as sqliteVec from "sqlite-vec";
|
|
9
|
+
let savepointSequence = 0;
|
|
9
10
|
function isBusyError(err) {
|
|
10
11
|
if (typeof err !== "object" || err === null)
|
|
11
12
|
return false;
|
|
@@ -18,12 +19,57 @@ function isBusyError(err) {
|
|
|
18
19
|
function sleepSync(ms) {
|
|
19
20
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
20
21
|
}
|
|
22
|
+
/** Synchronous SQLite connection with QMD-compatible transactions. */
|
|
23
|
+
export class Database extends DatabaseSync {
|
|
24
|
+
transaction(operation) {
|
|
25
|
+
const execute = (mode, args) => {
|
|
26
|
+
if (!this.isTransaction) {
|
|
27
|
+
this.exec(`BEGIN ${mode}`);
|
|
28
|
+
try {
|
|
29
|
+
const result = operation(...args);
|
|
30
|
+
this.exec("COMMIT");
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
try {
|
|
35
|
+
this.exec("ROLLBACK");
|
|
36
|
+
}
|
|
37
|
+
catch { }
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const savepoint = `qmd_${++savepointSequence}`;
|
|
42
|
+
this.exec(`SAVEPOINT ${savepoint}`);
|
|
43
|
+
try {
|
|
44
|
+
const result = operation(...args);
|
|
45
|
+
this.exec(`RELEASE ${savepoint}`);
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
try {
|
|
50
|
+
this.exec(`ROLLBACK TO ${savepoint}`);
|
|
51
|
+
this.exec(`RELEASE ${savepoint}`);
|
|
52
|
+
}
|
|
53
|
+
catch { }
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const transaction = ((...args) => execute("DEFERRED", args));
|
|
58
|
+
transaction.deferred = (...args) => execute("DEFERRED", args);
|
|
59
|
+
transaction.immediate = (...args) => execute("IMMEDIATE", args);
|
|
60
|
+
transaction.exclusive = (...args) => execute("EXCLUSIVE", args);
|
|
61
|
+
return transaction;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function resolveBusyTimeout() {
|
|
65
|
+
const raw = process.env.QMD_SQLITE_BUSY_TIMEOUT;
|
|
66
|
+
const parsed = raw !== undefined && raw !== "" ? Number(raw) : Number.NaN;
|
|
67
|
+
return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : 120_000;
|
|
68
|
+
}
|
|
21
69
|
/**
|
|
22
70
|
* Switch a connection to WAL, retrying on `SQLITE_BUSY` within the busy-timeout
|
|
23
|
-
* budget.
|
|
24
|
-
*
|
|
25
|
-
* cold database throw "database is locked" even with `busy_timeout` set. Once the
|
|
26
|
-
* database is already WAL the pragma is a cheap no-op that does not contend.
|
|
71
|
+
* budget. Migrating the journal needs a brief exclusive lock and does not invoke
|
|
72
|
+
* SQLite's busy handler on every supported runtime.
|
|
27
73
|
*/
|
|
28
74
|
function enableWal(db, budgetMs) {
|
|
29
75
|
const deadline = Date.now() + Math.max(budgetMs, 0);
|
|
@@ -39,46 +85,23 @@ function enableWal(db, budgetMs) {
|
|
|
39
85
|
}
|
|
40
86
|
}
|
|
41
87
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Open a SQLite database using better-sqlite3.
|
|
44
|
-
*
|
|
45
|
-
* `better-sqlite3` defaults `busy_timeout` to 0, so concurrent writers throw
|
|
46
|
-
* `SQLITE_BUSY` instead of waiting. WAL improves read-while-write concurrency
|
|
47
|
-
* but does not serialise writers. Setting the timeout at connection open makes
|
|
48
|
-
* parallel processes queue at batch boundaries instead of failing on contact.
|
|
49
|
-
*
|
|
50
|
-
* WAL is enabled here too (with a bounded retry) so connection-level pragmas
|
|
51
|
-
* live in one place and the cold-database journal migration survives concurrent
|
|
52
|
-
* opens.
|
|
53
|
-
*
|
|
54
|
-
* Default 120_000 ms outlasts the worst-case batch commit on a multi-GB
|
|
55
|
-
* index. Override with `QMD_SQLITE_BUSY_TIMEOUT` (value in milliseconds; `0`
|
|
56
|
-
* restores the upstream fail-fast behaviour).
|
|
57
|
-
*/
|
|
88
|
+
/** Open a writable QMD database using Node's built-in SQLite runtime. */
|
|
58
89
|
export function openDatabase(path) {
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
const parsed = raw !== undefined && raw !== "" ? Number(raw) : Number.NaN;
|
|
62
|
-
const busyTimeoutMs = Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : 120_000;
|
|
63
|
-
db.exec(`PRAGMA busy_timeout = ${busyTimeoutMs}`);
|
|
90
|
+
const busyTimeoutMs = resolveBusyTimeout();
|
|
91
|
+
const db = new Database(path, { allowExtension: true, timeout: busyTimeoutMs });
|
|
64
92
|
enableWal(db, busyTimeoutMs);
|
|
65
93
|
return db;
|
|
66
94
|
}
|
|
67
95
|
/** Open an existing database without changing journal mode, schema, or user data. */
|
|
68
96
|
export function openReadOnlyDatabase(path) {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return db;
|
|
97
|
+
const busyTimeoutMs = resolveBusyTimeout();
|
|
98
|
+
return new Database(path, {
|
|
99
|
+
readOnly: true,
|
|
100
|
+
allowExtension: true,
|
|
101
|
+
timeout: busyTimeoutMs,
|
|
102
|
+
});
|
|
76
103
|
}
|
|
77
|
-
/**
|
|
78
|
-
* Load the sqlite-vec extension into a database.
|
|
79
|
-
*
|
|
80
|
-
* Throws with fix instructions when the extension is unavailable.
|
|
81
|
-
*/
|
|
104
|
+
/** Load the sqlite-vec extension into a database. */
|
|
82
105
|
export function loadSqliteVec(db) {
|
|
83
106
|
try {
|
|
84
107
|
sqliteVec.load(db);
|
package/dist/store.js
CHANGED
|
@@ -1208,7 +1208,7 @@ function rebuildFTSForCjkNormalization(db) {
|
|
|
1208
1208
|
}
|
|
1209
1209
|
});
|
|
1210
1210
|
// Pull bounded batches and finalize each SELECT before starting the insert
|
|
1211
|
-
// transaction. Holding
|
|
1211
|
+
// transaction. Holding an SQLite iterator open while beginning a
|
|
1212
1212
|
// transaction on the same connection raises "database is busy".
|
|
1213
1213
|
const selectBatch = db.prepare(`
|
|
1214
1214
|
SELECT d.id, d.collection, d.path, d.title, content.doc as body
|
|
@@ -1532,6 +1532,12 @@ export function setStoreGlobalContext(db, value) {
|
|
|
1532
1532
|
db.prepare(`INSERT INTO store_config (key, value) VALUES ('global_context', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(value);
|
|
1533
1533
|
}
|
|
1534
1534
|
}
|
|
1535
|
+
function writeConfigSyncDiagnostic(db, diagnostic) {
|
|
1536
|
+
db.prepare(`
|
|
1537
|
+
INSERT INTO store_config (key, value) VALUES ('config_sync_diagnostic', ?)
|
|
1538
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
1539
|
+
`).run(JSON.stringify(diagnostic));
|
|
1540
|
+
}
|
|
1535
1541
|
function storeCollectionMatchesConfig(row, collection) {
|
|
1536
1542
|
return row.path === collection.path
|
|
1537
1543
|
&& row.pattern === (collection.pattern || '**/*.md')
|
|
@@ -1556,12 +1562,14 @@ export function syncConfigToDb(db, config) {
|
|
|
1556
1562
|
});
|
|
1557
1563
|
const currentGlobalContext = getStoreGlobalContext(db);
|
|
1558
1564
|
if (allMatch && currentGlobalContext === config.global_context) {
|
|
1559
|
-
|
|
1565
|
+
const diagnostic = {
|
|
1560
1566
|
configHashChanged: false,
|
|
1561
1567
|
reconciled: false,
|
|
1562
1568
|
collections: { added: [], updated: [], removed: [] },
|
|
1563
1569
|
globalContextUpdated: false,
|
|
1564
1570
|
};
|
|
1571
|
+
writeConfigSyncDiagnostic(db, diagnostic);
|
|
1572
|
+
return diagnostic;
|
|
1565
1573
|
}
|
|
1566
1574
|
}
|
|
1567
1575
|
}
|
|
@@ -2905,7 +2913,7 @@ export async function maybeAdoptLegacyEmbeddingFingerprint(store, model = DEFAUL
|
|
|
2905
2913
|
return { checked: true, adopted: 0, reason: `legacy sample differs from current fingerprint (nearest ${nearest.hash_seq}, distance ${nearest.distance.toFixed(6)})` };
|
|
2906
2914
|
}
|
|
2907
2915
|
const update = withLazyContentVectorMigration(db, () => db.prepare(`UPDATE content_vectors SET embed_fingerprint = ? WHERE model = ? AND embed_fingerprint = ''`).run(fingerprint, model));
|
|
2908
|
-
return { checked: true, adopted: update.changes, reason: `sample ${expectedHashSeq} matched current fingerprint at distance ${nearest.distance.toFixed(6)}` };
|
|
2916
|
+
return { checked: true, adopted: Number(update.changes), reason: `sample ${expectedHashSeq} matched current fingerprint at distance ${nearest.distance.toFixed(6)}` };
|
|
2909
2917
|
});
|
|
2910
2918
|
}
|
|
2911
2919
|
export function getIndexHealth(db, model = DEFAULT_EMBED_MODEL) {
|
|
@@ -2952,7 +2960,7 @@ export function clearCache(db) {
|
|
|
2952
2960
|
*/
|
|
2953
2961
|
export function deleteLLMCache(db) {
|
|
2954
2962
|
const result = db.prepare(`DELETE FROM llm_cache`).run();
|
|
2955
|
-
return result.changes;
|
|
2963
|
+
return Number(result.changes);
|
|
2956
2964
|
}
|
|
2957
2965
|
/**
|
|
2958
2966
|
* Remove inactive document records (active = 0).
|
|
@@ -2976,7 +2984,7 @@ export function cleanupOrphanedContent(db) {
|
|
|
2976
2984
|
DELETE FROM content
|
|
2977
2985
|
WHERE hash NOT IN (SELECT DISTINCT hash FROM documents)
|
|
2978
2986
|
`).run();
|
|
2979
|
-
return result.changes;
|
|
2987
|
+
return Number(result.changes);
|
|
2980
2988
|
}
|
|
2981
2989
|
/**
|
|
2982
2990
|
* Count content hashes that would be unreferenced after inactive documents
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wei840222/qmd",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.9.6",
|
|
4
4
|
"packageManager": "pnpm@11.15.1",
|
|
5
5
|
"description": "Query Markup Documents - On-device hybrid search for markdown files with BM25, vector search, and LLM reranking",
|
|
6
6
|
"type": "module",
|
|
@@ -66,7 +66,6 @@
|
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@modelcontextprotocol/server": "2.0.0",
|
|
68
68
|
"@node-rs/jieba": "2.0.2",
|
|
69
|
-
"better-sqlite3": "^13.0.3",
|
|
70
69
|
"fast-glob": "3.3.3",
|
|
71
70
|
"node-llama-cpp": "3.20.0",
|
|
72
71
|
"picomatch": "4.0.5",
|
|
@@ -88,7 +87,6 @@
|
|
|
88
87
|
},
|
|
89
88
|
"devDependencies": {
|
|
90
89
|
"@oxlint/plugins": "1.78.0",
|
|
91
|
-
"@types/better-sqlite3": "7.6.13",
|
|
92
90
|
"oxlint": "1.78.0",
|
|
93
91
|
"tsx": "4.23.12",
|
|
94
92
|
"vitest": "3.2.7"
|
|
@@ -113,7 +111,7 @@
|
|
|
113
111
|
"typescript": "^5.9.3 || ^6.0.0-0"
|
|
114
112
|
},
|
|
115
113
|
"engines": {
|
|
116
|
-
"node": ">=22.
|
|
114
|
+
"node": ">=22.16.0"
|
|
117
115
|
},
|
|
118
116
|
"keywords": [
|
|
119
117
|
"markdown",
|
package/skills/qmd/SKILL.md
CHANGED
|
@@ -230,8 +230,6 @@ Query types:
|
|
|
230
230
|
- `vec` — vector semantic search. Best for natural-language concepts.
|
|
231
231
|
- `hyde` — vector search using a hypothetical answer/document passage.
|
|
232
232
|
|
|
233
|
-
When invoking `query` with a plain `query` string instead of explicit `searches`, you can set `includeHyde: false` (to omit HyDE passage generation) and `expansion: "auto" | "force" | "skip"`.
|
|
234
|
-
|
|
235
233
|
## Query craft
|
|
236
234
|
|
|
237
235
|
Good QMD searches mix three things:
|
|
@@ -30,7 +30,6 @@ newline = "\n" ;
|
|
|
30
30
|
|
|
31
31
|
A query is either a single policy query or a multi-line query document:
|
|
32
32
|
- **`auto` (Default)**: CJK queries and strong lexical matches automatically bypass model expansion. Other plain queries expand into `lex`, `vec`, and `hyde` variants.
|
|
33
|
-
- **`--no-hyde`**: Disables HyDE (hypothetical document) in query expansion, generating only `lex` and `vec` variants (faster, avoids hallucinated passage drift).
|
|
34
33
|
- **`expand:` / `--expand` (`force`)**: Explicitly forces expansion even if bypass heuristics apply.
|
|
35
34
|
- **`lex:` (`skip`)**: Explicitly disables expansion and performs direct BM25 search.
|
|
36
35
|
|
|
@@ -38,9 +37,6 @@ A query is either a single policy query or a multi-line query document:
|
|
|
38
37
|
# Automatic policy:
|
|
39
38
|
qmd query "how does authentication work"
|
|
40
39
|
|
|
41
|
-
# Disable HyDE during expansion:
|
|
42
|
-
qmd query --no-hyde "how does authentication work"
|
|
43
|
-
|
|
44
40
|
# Force expansion:
|
|
45
41
|
qmd query "expand: how does authentication work"
|
|
46
42
|
# or: qmd query --expand "資料庫同步"
|
|
@@ -85,8 +81,6 @@ A 50–100 word hypothetical answer passage representing what the target documen
|
|
|
85
81
|
hyde: The rate limiter uses a sliding window counter algorithm with a 60-second window. When a client exceeds 100 requests per minute, subsequent requests return 429 Too Many Requests.
|
|
86
82
|
```
|
|
87
83
|
|
|
88
|
-
When relying on query expansion, HyDE generation can be excluded using `--no-hyde` (CLI) or `"includeHyde": false` (MCP/SDK).
|
|
89
|
-
|
|
90
84
|
## Multi-Line Structured Queries
|
|
91
85
|
|
|
92
86
|
Combine multiple sub-query types for optimal retrieval. The first sub-query receives **2x weight** during Reciprocal Rank Fusion:
|
|
@@ -156,9 +150,8 @@ When calling the `qmd` MCP server's `query` tool, provide a structured `searches
|
|
|
156
150
|
|
|
157
151
|
```json
|
|
158
152
|
{
|
|
159
|
-
"query": "
|
|
153
|
+
"query": "CAP theorem consistency",
|
|
160
154
|
"expansion": "auto",
|
|
161
|
-
"includeHyde": false,
|
|
162
155
|
"explain": true,
|
|
163
156
|
"collections": ["docs"]
|
|
164
157
|
}
|