@tobilu/qmd 2.1.0 → 2.5.1
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 +81 -0
- package/README.md +3 -0
- package/bin/qmd +39 -3
- package/dist/ast.d.ts +1 -0
- package/dist/ast.js +18 -8
- package/dist/bench/bench.d.ts +2 -0
- package/dist/bench/bench.js +108 -13
- package/dist/bench/score.d.ts +11 -4
- package/dist/bench/score.js +34 -13
- package/dist/bench/types.d.ts +13 -0
- package/dist/cli/qmd.d.ts +26 -0
- package/dist/cli/qmd.js +1172 -121
- package/dist/collections.d.ts +9 -0
- package/dist/collections.js +32 -7
- package/dist/db.d.ts +6 -3
- package/dist/db.js +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -2
- package/dist/llm.d.ts +65 -3
- package/dist/llm.js +376 -63
- package/dist/mcp/server.d.ts +6 -3
- package/dist/mcp/server.js +41 -26
- package/dist/paths.d.ts +1 -0
- package/dist/paths.js +4 -0
- package/dist/store.d.ts +92 -17
- package/dist/store.js +676 -176
- package/package.json +23 -12
- package/scripts/build.mjs +29 -0
- package/scripts/check-package-grammars.mjs +29 -0
- package/scripts/package-smoke.mjs +65 -0
- package/scripts/test-all.mjs +27 -0
- package/skills/qmd/SKILL.md +203 -0
- package/skills/qmd/references/mcp-setup.md +102 -0
- package/skills/release/SKILL.md +139 -0
- package/skills/release/scripts/install-hooks.sh +38 -0
- package/dist/embedded-skills.d.ts +0 -6
- package/dist/embedded-skills.js +0 -14
package/dist/collections.d.ts
CHANGED
|
@@ -60,6 +60,15 @@ export declare function setConfigSource(source?: {
|
|
|
60
60
|
* Config file will be ~/.config/qmd/{indexName}.yml
|
|
61
61
|
*/
|
|
62
62
|
export declare function setConfigIndexName(name: string): void;
|
|
63
|
+
/**
|
|
64
|
+
* Find a project-local QMD config by walking upward from startDir.
|
|
65
|
+
* The local config lives at .qmd/index.yaml or .qmd/index.yml and,
|
|
66
|
+
* when used by the CLI, keeps both config and index DB writes inside
|
|
67
|
+
* the project instead of the global ~/.config / ~/.cache locations.
|
|
68
|
+
*/
|
|
69
|
+
export declare function findLocalConfigPath(startDir?: string): string | undefined;
|
|
70
|
+
/** Return the local SQLite index path paired with a local .qmd/index.yaml file. */
|
|
71
|
+
export declare function getLocalDbPath(configPath: string): string;
|
|
63
72
|
/**
|
|
64
73
|
* Load configuration from the configured source.
|
|
65
74
|
* - Inline config: returns the in-memory object directly
|
package/dist/collections.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Collections define which directories to index and their associated contexts.
|
|
6
6
|
*/
|
|
7
7
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
8
|
-
import { join, dirname } from "path";
|
|
9
|
-
import {
|
|
8
|
+
import { join, dirname, resolve } from "path";
|
|
9
|
+
import { qmdHomedir } from "./paths.js";
|
|
10
10
|
import YAML from "yaml";
|
|
11
11
|
// ============================================================================
|
|
12
12
|
// Configuration paths
|
|
@@ -47,9 +47,7 @@ export function setConfigSource(source) {
|
|
|
47
47
|
export function setConfigIndexName(name) {
|
|
48
48
|
// Resolve relative paths to absolute paths and sanitize for use as filename
|
|
49
49
|
if (name.includes('/')) {
|
|
50
|
-
const
|
|
51
|
-
const { cwd } = require('process');
|
|
52
|
-
const absolutePath = resolve(cwd(), name);
|
|
50
|
+
const absolutePath = resolve(process.cwd(), name);
|
|
53
51
|
// Replace path separators with underscores to create a valid filename
|
|
54
52
|
currentIndexName = absolutePath.replace(/\//g, '_').replace(/^_/, '');
|
|
55
53
|
}
|
|
@@ -66,11 +64,37 @@ function getConfigDir() {
|
|
|
66
64
|
if (process.env.XDG_CONFIG_HOME) {
|
|
67
65
|
return join(process.env.XDG_CONFIG_HOME, "qmd");
|
|
68
66
|
}
|
|
69
|
-
return join(
|
|
67
|
+
return join(qmdHomedir(), ".config", "qmd");
|
|
70
68
|
}
|
|
71
69
|
function getConfigFilePath() {
|
|
72
70
|
return join(getConfigDir(), `${currentIndexName}.yml`);
|
|
73
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Find a project-local QMD config by walking upward from startDir.
|
|
74
|
+
* The local config lives at .qmd/index.yaml or .qmd/index.yml and,
|
|
75
|
+
* when used by the CLI, keeps both config and index DB writes inside
|
|
76
|
+
* the project instead of the global ~/.config / ~/.cache locations.
|
|
77
|
+
*/
|
|
78
|
+
export function findLocalConfigPath(startDir = process.cwd()) {
|
|
79
|
+
let dir = resolve(startDir);
|
|
80
|
+
while (true) {
|
|
81
|
+
const qmdDir = join(dir, ".qmd");
|
|
82
|
+
const yamlPath = join(qmdDir, "index.yaml");
|
|
83
|
+
if (existsSync(yamlPath))
|
|
84
|
+
return yamlPath;
|
|
85
|
+
const ymlPath = join(qmdDir, "index.yml");
|
|
86
|
+
if (existsSync(ymlPath))
|
|
87
|
+
return ymlPath;
|
|
88
|
+
const parent = dirname(dir);
|
|
89
|
+
if (parent === dir)
|
|
90
|
+
return undefined;
|
|
91
|
+
dir = parent;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** Return the local SQLite index path paired with a local .qmd/index.yaml file. */
|
|
95
|
+
export function getLocalDbPath(configPath) {
|
|
96
|
+
return join(dirname(configPath), "index.sqlite");
|
|
97
|
+
}
|
|
74
98
|
/**
|
|
75
99
|
* Ensure config directory exists
|
|
76
100
|
*/
|
|
@@ -101,7 +125,8 @@ export function loadConfig() {
|
|
|
101
125
|
}
|
|
102
126
|
try {
|
|
103
127
|
const content = readFileSync(configPath, "utf-8");
|
|
104
|
-
const
|
|
128
|
+
const parsed = YAML.parse(content);
|
|
129
|
+
const config = parsed ?? { collections: {} };
|
|
105
130
|
// Ensure collections object exists
|
|
106
131
|
if (!config.collections) {
|
|
107
132
|
config.collections = {};
|
package/dist/db.d.ts
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* SQLite build before creating any database instances.
|
|
12
12
|
*/
|
|
13
13
|
export declare const isBun: boolean;
|
|
14
|
+
export type SQLiteValue = string | number | bigint | Buffer | Uint8Array | Float32Array | null;
|
|
15
|
+
export type SQLiteParams = readonly SQLiteValue[];
|
|
14
16
|
/**
|
|
15
17
|
* Open a SQLite database. Works with both bun:sqlite and better-sqlite3.
|
|
16
18
|
*/
|
|
@@ -22,15 +24,16 @@ export interface Database {
|
|
|
22
24
|
exec(sql: string): void;
|
|
23
25
|
prepare(sql: string): Statement;
|
|
24
26
|
loadExtension(path: string): void;
|
|
27
|
+
transaction<T extends (...args: SQLiteValue[]) => unknown>(fn: T): T;
|
|
25
28
|
close(): void;
|
|
26
29
|
}
|
|
27
30
|
export interface Statement {
|
|
28
|
-
run(...params:
|
|
31
|
+
run(...params: SQLiteValue[]): {
|
|
29
32
|
changes: number;
|
|
30
33
|
lastInsertRowid: number | bigint;
|
|
31
34
|
};
|
|
32
|
-
get(...params:
|
|
33
|
-
all(...params:
|
|
35
|
+
get<T = unknown>(...params: SQLiteValue[]): T | undefined;
|
|
36
|
+
all<T = unknown>(...params: SQLiteValue[]): T[];
|
|
34
37
|
}
|
|
35
38
|
/**
|
|
36
39
|
* Load the sqlite-vec extension into a database.
|
package/dist/db.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Bun we call Database.setCustomSQLite() to swap in Homebrew's full-featured
|
|
11
11
|
* SQLite build before creating any database instances.
|
|
12
12
|
*/
|
|
13
|
-
export const isBun =
|
|
13
|
+
export const isBun = "Bun" in globalThis;
|
|
14
14
|
let _Database;
|
|
15
15
|
let _sqliteVecLoad;
|
|
16
16
|
if (isBun) {
|
package/dist/index.d.ts
CHANGED
|
@@ -62,6 +62,8 @@ export interface SearchOptions {
|
|
|
62
62
|
collections?: string[];
|
|
63
63
|
/** Max results (default: 10) */
|
|
64
64
|
limit?: number;
|
|
65
|
+
/** Max candidates to rerank (default: 40) */
|
|
66
|
+
candidateLimit?: number;
|
|
65
67
|
/** Minimum score threshold */
|
|
66
68
|
minScore?: number;
|
|
67
69
|
/** Include explain traces */
|
|
@@ -186,6 +188,8 @@ export interface QMDStore {
|
|
|
186
188
|
embed(options?: {
|
|
187
189
|
force?: boolean;
|
|
188
190
|
model?: string;
|
|
191
|
+
/** Restrict embedding to documents in one collection. */
|
|
192
|
+
collection?: string;
|
|
189
193
|
maxDocsPerBatch?: number;
|
|
190
194
|
maxBatchBytes?: number;
|
|
191
195
|
chunkStrategy?: ChunkStrategy;
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* const results = await store.search({ query: "how does auth work?" })
|
|
17
17
|
* await store.close()
|
|
18
18
|
*/
|
|
19
|
-
import { createStore as createStoreInternal, hybridQuery, structuredSearch, extractSnippet, addLineNumbers,
|
|
19
|
+
import { createStore as createStoreInternal, hybridQuery, structuredSearch, extractSnippet, addLineNumbers, DEFAULT_MULTI_GET_MAX_BYTES, reindexCollection, generateEmbeddings, listCollections as storeListCollections, syncConfigToDb, getStoreCollections, getStoreCollection, getStoreGlobalContext, getStoreContexts, upsertStoreCollection, deleteStoreCollection, renameStoreCollection, updateStoreContext, removeStoreContext, setStoreGlobalContext, vacuumDatabase, cleanupOrphanedContent, cleanupOrphanedVectors, deleteLLMCache, deleteInactiveDocuments, clearAllEmbeddings, } from "./store.js";
|
|
20
20
|
import { LlamaCpp, } from "./llm.js";
|
|
21
21
|
import { setConfigSource, loadConfig, addCollection as collectionsAddCollection, removeCollection as collectionsRemoveCollection, renameCollection as collectionsRenameCollection, addContext as collectionsAddContext, removeContext as collectionsRemoveContext, setGlobalContext as collectionsSetGlobalContext, } from "./collections.js";
|
|
22
22
|
// Re-export utility functions and types used by frontends
|
|
@@ -109,6 +109,7 @@ export async function createStore(options) {
|
|
|
109
109
|
minScore: opts.minScore,
|
|
110
110
|
explain: opts.explain,
|
|
111
111
|
intent: opts.intent,
|
|
112
|
+
candidateLimit: opts.candidateLimit,
|
|
112
113
|
skipRerank,
|
|
113
114
|
chunkStrategy: opts.chunkStrategy,
|
|
114
115
|
});
|
|
@@ -120,12 +121,13 @@ export async function createStore(options) {
|
|
|
120
121
|
minScore: opts.minScore,
|
|
121
122
|
explain: opts.explain,
|
|
122
123
|
intent: opts.intent,
|
|
124
|
+
candidateLimit: opts.candidateLimit,
|
|
123
125
|
skipRerank,
|
|
124
126
|
chunkStrategy: opts.chunkStrategy,
|
|
125
127
|
});
|
|
126
128
|
},
|
|
127
129
|
searchLex: async (q, opts) => internal.searchFTS(q, opts?.limit, opts?.collection),
|
|
128
|
-
searchVector: async (q, opts) => internal.searchVec(q,
|
|
130
|
+
searchVector: async (q, opts) => internal.searchVec(q, llm.embedModelName, opts?.limit, opts?.collection),
|
|
129
131
|
expandQuery: async (q, opts) => internal.expandQuery(q, undefined, opts?.intent),
|
|
130
132
|
get: async (pathOrDocid, opts) => internal.findDocument(pathOrDocid, opts),
|
|
131
133
|
getDocumentBody: async (pathOrDocid, opts) => {
|
|
@@ -217,6 +219,7 @@ export async function createStore(options) {
|
|
|
217
219
|
return generateEmbeddings(internal, {
|
|
218
220
|
force: embedOpts?.force,
|
|
219
221
|
model: embedOpts?.model,
|
|
222
|
+
collection: embedOpts?.collection,
|
|
220
223
|
maxDocsPerBatch: embedOpts?.maxDocsPerBatch,
|
|
221
224
|
maxBatchBytes: embedOpts?.maxBatchBytes,
|
|
222
225
|
chunkStrategy: embedOpts?.chunkStrategy,
|
package/dist/llm.d.ts
CHANGED
|
@@ -3,7 +3,27 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides embeddings, text generation, and reranking using local GGUF models.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import type { Llama, Token as LlamaToken } from "node-llama-cpp";
|
|
7
|
+
type NodeLlamaCppModule = {
|
|
8
|
+
getLlama: (options: Record<string, unknown>) => Promise<Llama>;
|
|
9
|
+
getLlamaGpuTypes?: (include?: "supported" | "allValid") => Promise<LlamaGpuMode[]>;
|
|
10
|
+
resolveModelFile: (model: string, cacheDir: string) => Promise<string>;
|
|
11
|
+
LlamaChatSession: new (options: {
|
|
12
|
+
contextSequence: unknown;
|
|
13
|
+
}) => {
|
|
14
|
+
prompt: (prompt: string, options?: Record<string, unknown>) => Promise<string>;
|
|
15
|
+
};
|
|
16
|
+
LlamaLogLevel: {
|
|
17
|
+
error: unknown;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export declare function setNodeLlamaCppModuleForTest(module: NodeLlamaCppModule | null): void;
|
|
21
|
+
/**
|
|
22
|
+
* Some node-llama-cpp native build/probe paths write library noise to stdout.
|
|
23
|
+
* JSON APIs must reserve stdout for machine-readable payloads, so route that
|
|
24
|
+
* noise to stderr while native llama initialization is in progress.
|
|
25
|
+
*/
|
|
26
|
+
export declare function withNativeStdoutRedirectedToStderr<T>(fn: () => Promise<T>): Promise<T>;
|
|
7
27
|
/**
|
|
8
28
|
* Detect if a model URI uses the Qwen3-Embedding format.
|
|
9
29
|
* Qwen3-Embedding uses a different prompting style than nomic/embeddinggemma.
|
|
@@ -140,6 +160,15 @@ export declare const LFM2_INSTRUCT_MODEL = "hf:LiquidAI/LFM2.5-1.2B-Instruct-GGU
|
|
|
140
160
|
export declare const DEFAULT_EMBED_MODEL_URI = "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf";
|
|
141
161
|
export declare const DEFAULT_RERANK_MODEL_URI = "hf:ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF/qwen3-reranker-0.6b-q8_0.gguf";
|
|
142
162
|
export declare const DEFAULT_GENERATE_MODEL_URI = "hf:tobil/qmd-query-expansion-1.7B-gguf/qmd-query-expansion-1.7B-q4_k_m.gguf";
|
|
163
|
+
export type ModelResolutionConfig = {
|
|
164
|
+
embed?: string;
|
|
165
|
+
generate?: string;
|
|
166
|
+
rerank?: string;
|
|
167
|
+
};
|
|
168
|
+
export declare function resolveEmbedModel(config?: ModelResolutionConfig): string;
|
|
169
|
+
export declare function resolveGenerateModel(config?: ModelResolutionConfig): string;
|
|
170
|
+
export declare function resolveRerankModel(config?: ModelResolutionConfig): string;
|
|
171
|
+
export declare function resolveModels(config?: ModelResolutionConfig): Required<ModelResolutionConfig>;
|
|
143
172
|
export declare const DEFAULT_MODEL_CACHE_DIR: string;
|
|
144
173
|
export type PullResult = {
|
|
145
174
|
model: string;
|
|
@@ -147,6 +176,19 @@ export type PullResult = {
|
|
|
147
176
|
sizeBytes: number;
|
|
148
177
|
refreshed: boolean;
|
|
149
178
|
};
|
|
179
|
+
export type GgufFileInspection = {
|
|
180
|
+
exists: boolean;
|
|
181
|
+
valid: boolean;
|
|
182
|
+
kind: "missing" | "gguf" | "html" | "invalid";
|
|
183
|
+
sizeBytes?: number;
|
|
184
|
+
magic?: string;
|
|
185
|
+
details: string;
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* Inspect a potential GGUF model file without mutating it.
|
|
189
|
+
* Used by doctor for early diagnostics and by runtime validation before load.
|
|
190
|
+
*/
|
|
191
|
+
export declare function inspectGgufFile(filePath: string): GgufFileInspection;
|
|
150
192
|
export declare function pullModels(models: string[], options?: {
|
|
151
193
|
refresh?: boolean;
|
|
152
194
|
cacheDir?: string;
|
|
@@ -211,6 +253,16 @@ export type LlamaCppConfig = {
|
|
|
211
253
|
*/
|
|
212
254
|
disposeModelsOnInactivity?: boolean;
|
|
213
255
|
};
|
|
256
|
+
export type LlamaGpuMode = "auto" | "metal" | "vulkan" | "cuda" | false;
|
|
257
|
+
type ParallelismOptions = {
|
|
258
|
+
gpu: string | false;
|
|
259
|
+
platform?: NodeJS.Platform;
|
|
260
|
+
computed: number;
|
|
261
|
+
envValue?: string;
|
|
262
|
+
};
|
|
263
|
+
export declare function resolveParallelismOverride(envValue?: string | undefined): number | undefined;
|
|
264
|
+
export declare function resolveSafeParallelism(options: ParallelismOptions): number;
|
|
265
|
+
export declare function resolveLlamaGpuMode(envValue?: string | undefined, forceCpuValue?: string | undefined): LlamaGpuMode;
|
|
214
266
|
export declare class LlamaCpp implements LLM {
|
|
215
267
|
private readonly _ciMode;
|
|
216
268
|
private llama;
|
|
@@ -233,6 +285,8 @@ export declare class LlamaCpp implements LLM {
|
|
|
233
285
|
private disposed;
|
|
234
286
|
constructor(config?: LlamaCppConfig);
|
|
235
287
|
get embedModelName(): string;
|
|
288
|
+
get generateModelName(): string;
|
|
289
|
+
get rerankModelName(): string;
|
|
236
290
|
/**
|
|
237
291
|
* Reset the inactivity timer. Called after each model operation.
|
|
238
292
|
* When timer fires, models are unloaded to free memory (if no active sessions).
|
|
@@ -257,8 +311,12 @@ export declare class LlamaCpp implements LLM {
|
|
|
257
311
|
* Initialize the llama instance (lazy)
|
|
258
312
|
*/
|
|
259
313
|
private ensureLlama;
|
|
314
|
+
private isCpuOffloadForced;
|
|
315
|
+
private modelLoadOptions;
|
|
260
316
|
/**
|
|
261
|
-
* Resolve a model URI to a local path, downloading if needed
|
|
317
|
+
* Resolve a model URI to a local path, downloading if needed.
|
|
318
|
+
* Validates the downloaded file is actually a GGUF model (not an HTML error page
|
|
319
|
+
* from a proxy or firewall).
|
|
262
320
|
*/
|
|
263
321
|
private resolveModel;
|
|
264
322
|
/**
|
|
@@ -328,6 +386,7 @@ export declare class LlamaCpp implements LLM {
|
|
|
328
386
|
* detokenizes back to text if truncation is needed.
|
|
329
387
|
* Returns the (possibly truncated) text and whether truncation occurred.
|
|
330
388
|
*/
|
|
389
|
+
private resolveEmbedTokenLimit;
|
|
331
390
|
private truncateToContextSize;
|
|
332
391
|
embed(text: string, options?: EmbedOptions): Promise<EmbeddingResult | null>;
|
|
333
392
|
/**
|
|
@@ -349,7 +408,9 @@ export declare class LlamaCpp implements LLM {
|
|
|
349
408
|
* Get device/GPU info for status display.
|
|
350
409
|
* Initializes llama if not already done.
|
|
351
410
|
*/
|
|
352
|
-
getDeviceInfo(
|
|
411
|
+
getDeviceInfo(options?: {
|
|
412
|
+
allowBuild?: boolean;
|
|
413
|
+
}): Promise<{
|
|
353
414
|
gpu: string | false;
|
|
354
415
|
gpuOffloading: boolean;
|
|
355
416
|
gpuDevices: string[];
|
|
@@ -406,3 +467,4 @@ export declare function setDefaultLlamaCpp(llm: LlamaCpp | null): void;
|
|
|
406
467
|
* Call this before process exit to prevent NAPI crashes.
|
|
407
468
|
*/
|
|
408
469
|
export declare function disposeDefaultLlamaCpp(): Promise<void>;
|
|
470
|
+
export {};
|