@gmickel/gno 1.2.1 → 1.3.0
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/README.md +1 -1
- package/assets/skill/SKILL.md +3 -0
- package/assets/skill/cli-reference.md +5 -0
- package/assets/skill/examples.md +2 -0
- package/package.json +1 -1
- package/src/app/constants.ts +64 -8
- package/src/cli/commands/embed.ts +6 -2
- package/src/cli/commands/get.ts +15 -5
- package/src/cli/commands/index-cmd.ts +4 -0
- package/src/cli/commands/multi-get.ts +62 -1
- package/src/cli/commands/query.ts +8 -2
- package/src/cli/commands/search.ts +8 -2
- package/src/cli/commands/shared.ts +18 -1
- package/src/cli/commands/status.ts +4 -2
- package/src/cli/commands/update.ts +6 -1
- package/src/cli/commands/vsearch.ts +8 -2
- package/src/cli/format/search-results.ts +1 -1
- package/src/cli/program.ts +22 -1
- package/src/ingestion/chunker.ts +6 -0
- package/src/llm/cache.ts +133 -27
- package/src/llm/errors.ts +32 -0
- package/src/llm/nodeLlamaCpp/embedding.ts +69 -3
- package/src/llm/nodeLlamaCpp/lifecycle.ts +60 -4
- package/src/mcp/resources/index.ts +13 -4
- package/src/mcp/server.ts +2 -0
- package/src/mcp/tools/get.ts +7 -2
- package/src/mcp/tools/multi-get.ts +2 -2
- package/src/mcp/tools/query.ts +2 -1
- package/src/mcp/tools/search.ts +2 -1
- package/src/mcp/tools/vsearch.ts +2 -1
- package/src/pipeline/explain.ts +12 -2
- package/src/pipeline/hybrid.ts +9 -1
- package/src/pipeline/search.ts +1 -0
- package/src/pipeline/types.ts +2 -0
- package/src/pipeline/vsearch.ts +14 -8
- package/src/sdk/client.ts +83 -28
- package/src/store/sqlite/adapter.ts +3 -2
- package/src/store/vector/sqlite-vec.ts +10 -4
- package/src/store/vector/types.ts +2 -0
package/README.md
CHANGED
|
@@ -778,7 +778,7 @@ graph TD
|
|
|
778
778
|
|
|
779
779
|
## Local Models
|
|
780
780
|
|
|
781
|
-
Models auto-download on first use to `~/.cache/gno/models/`. For deterministic startup, set `GNO_NO_AUTO_DOWNLOAD=1` and use `gno models pull` explicitly. Alternatively, offload to a GPU server on your network using HTTP backends.
|
|
781
|
+
Models auto-download on first use to `~/.cache/gno/models/`. GNO validates cached GGUF files before loading and removes intercepted HTML/non-GGUF cache entries with a clear recovery error. For deterministic startup, set `GNO_NO_AUTO_DOWNLOAD=1` and use `gno models pull` explicitly. Alternatively, offload to a GPU server on your network using HTTP backends.
|
|
782
782
|
|
|
783
783
|
| Model | Purpose | Size |
|
|
784
784
|
| :--------------------- | :------------------------------------ | :----------- |
|
package/assets/skill/SKILL.md
CHANGED
|
@@ -167,6 +167,9 @@ gno graph -c notes --similar # Include similarity edges
|
|
|
167
167
|
--no-pager Disable paging
|
|
168
168
|
```
|
|
169
169
|
|
|
170
|
+
Non-default index search results may include `?index=<name>` on `gno://` URIs.
|
|
171
|
+
Keep that query string when passing the URI to `gno get`.
|
|
172
|
+
|
|
170
173
|
## Important: Embedding After Changes
|
|
171
174
|
|
|
172
175
|
If you edit/create files that should be searchable via vector search:
|
|
@@ -196,6 +196,7 @@ gno get <ref> [--from <line>] [-l <lines>] [--line-numbers] [--source]
|
|
|
196
196
|
Ref formats:
|
|
197
197
|
|
|
198
198
|
- `gno://collection/path` — Full URI
|
|
199
|
+
- `gno://collection/path?index=name` — Full URI emitted from a non-default index
|
|
199
200
|
- `collection/path` — Relative path
|
|
200
201
|
- `#docid` — Document ID
|
|
201
202
|
- `gno://docs/file.md:120` — With line number
|
|
@@ -378,6 +379,10 @@ Presets: `slim` (~1GB), `balanced` (~2GB), `quality` (~2.5GB)
|
|
|
378
379
|
gno models pull [--all|--embed|--rerank|--gen] [--force]
|
|
379
380
|
```
|
|
380
381
|
|
|
382
|
+
Cached/local model files are validated as GGUF before use. If the cache contains
|
|
383
|
+
HTML or another non-GGUF response, rerun with `--force` after fixing network
|
|
384
|
+
access.
|
|
385
|
+
|
|
381
386
|
### gno models clear
|
|
382
387
|
|
|
383
388
|
```bash
|
package/assets/skill/examples.md
CHANGED
|
@@ -90,6 +90,7 @@ gno ask "summarize project goals" --answer --max-answer-tokens 200
|
|
|
90
90
|
```bash
|
|
91
91
|
# By URI
|
|
92
92
|
gno get gno://work/readme.md
|
|
93
|
+
gno get "gno://work/readme.md?index=research"
|
|
93
94
|
|
|
94
95
|
# By document ID
|
|
95
96
|
gno get "#a1b2c3d4"
|
|
@@ -204,6 +205,7 @@ gno models use quality
|
|
|
204
205
|
|
|
205
206
|
# Download models
|
|
206
207
|
gno models pull
|
|
208
|
+
gno models pull --force
|
|
207
209
|
```
|
|
208
210
|
|
|
209
211
|
## Note Linking
|
package/package.json
CHANGED
package/src/app/constants.ts
CHANGED
|
@@ -244,22 +244,39 @@ export function getModelsCachePath(dirs: ResolvedDirs = resolveDirs()): string {
|
|
|
244
244
|
/**
|
|
245
245
|
* Build a gno:// URI from collection and relative path.
|
|
246
246
|
*/
|
|
247
|
-
export
|
|
247
|
+
export interface BuildUriOptions {
|
|
248
|
+
indexName?: string;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface ParsedGnoUri {
|
|
252
|
+
collection: string;
|
|
253
|
+
path: string;
|
|
254
|
+
indexName?: string;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function buildUri(
|
|
258
|
+
collection: string,
|
|
259
|
+
relativePath: string,
|
|
260
|
+
options: BuildUriOptions = {}
|
|
261
|
+
): string {
|
|
248
262
|
// URL-encode special chars in path segments but preserve slashes
|
|
249
263
|
const encodedPath = relativePath
|
|
250
264
|
.split("/")
|
|
251
265
|
.map((segment) => encodeURIComponent(segment))
|
|
252
266
|
.join("/");
|
|
253
|
-
|
|
267
|
+
const uri = `${URI_PREFIX}${collection}/${encodedPath}`;
|
|
268
|
+
const indexName = options.indexName?.trim();
|
|
269
|
+
if (!indexName || indexName === DEFAULT_INDEX_NAME) {
|
|
270
|
+
return uri;
|
|
271
|
+
}
|
|
272
|
+
return `${uri}?index=${encodeURIComponent(indexName)}`;
|
|
254
273
|
}
|
|
255
274
|
|
|
256
275
|
/**
|
|
257
276
|
* Parse a gno:// URI into collection and path components.
|
|
258
277
|
* Returns null if not a valid gno:// URI or if decoding fails.
|
|
259
278
|
*/
|
|
260
|
-
export function parseUri(
|
|
261
|
-
uri: string
|
|
262
|
-
): { collection: string; path: string } | null {
|
|
279
|
+
export function parseUri(uri: string): ParsedGnoUri | null {
|
|
263
280
|
if (!uri.startsWith(URI_PREFIX)) {
|
|
264
281
|
return null;
|
|
265
282
|
}
|
|
@@ -269,20 +286,59 @@ export function parseUri(
|
|
|
269
286
|
|
|
270
287
|
if (slashIndex === -1) {
|
|
271
288
|
// gno://collection (no path)
|
|
272
|
-
|
|
289
|
+
const [collectionWithQuery, query = ""] = rest.split("?", 2);
|
|
290
|
+
const indexName = new URLSearchParams(query).get("index")?.trim();
|
|
291
|
+
return indexName
|
|
292
|
+
? {
|
|
293
|
+
collection: collectionWithQuery ?? rest,
|
|
294
|
+
path: "",
|
|
295
|
+
indexName,
|
|
296
|
+
}
|
|
297
|
+
: { collection: collectionWithQuery ?? rest, path: "" };
|
|
273
298
|
}
|
|
274
299
|
|
|
275
300
|
const collection = rest.slice(0, slashIndex);
|
|
301
|
+
const pathAndQuery = rest.slice(slashIndex + 1);
|
|
302
|
+
const queryIndex = pathAndQuery.indexOf("?");
|
|
303
|
+
const encodedPath =
|
|
304
|
+
queryIndex === -1 ? pathAndQuery : pathAndQuery.slice(0, queryIndex);
|
|
305
|
+
const query = queryIndex === -1 ? "" : pathAndQuery.slice(queryIndex + 1);
|
|
276
306
|
|
|
277
307
|
// decodeURIComponent throws on malformed percent-encoding
|
|
278
308
|
try {
|
|
279
|
-
const path = decodeURIComponent(
|
|
280
|
-
|
|
309
|
+
const path = decodeURIComponent(encodedPath);
|
|
310
|
+
const indexName = new URLSearchParams(query).get("index")?.trim();
|
|
311
|
+
return indexName ? { collection, path, indexName } : { collection, path };
|
|
281
312
|
} catch {
|
|
282
313
|
return null;
|
|
283
314
|
}
|
|
284
315
|
}
|
|
285
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Add output-only index metadata to a canonical gno:// URI.
|
|
319
|
+
*/
|
|
320
|
+
export function decorateUriForIndex(uri: string, indexName?: string): string {
|
|
321
|
+
const parsed = parseUri(uri);
|
|
322
|
+
const normalizedIndex = indexName?.trim();
|
|
323
|
+
if (!parsed || !normalizedIndex || normalizedIndex === DEFAULT_INDEX_NAME) {
|
|
324
|
+
return stripUriIndex(uri);
|
|
325
|
+
}
|
|
326
|
+
return buildUri(parsed.collection, parsed.path, {
|
|
327
|
+
indexName: normalizedIndex,
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Remove output-only index metadata from a gno:// URI.
|
|
333
|
+
*/
|
|
334
|
+
export function stripUriIndex(uri: string): string {
|
|
335
|
+
const parsed = parseUri(uri);
|
|
336
|
+
if (!parsed) {
|
|
337
|
+
return uri;
|
|
338
|
+
}
|
|
339
|
+
return buildUri(parsed.collection, parsed.path);
|
|
340
|
+
}
|
|
341
|
+
|
|
286
342
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
287
343
|
// docid Utilities
|
|
288
344
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -45,6 +45,8 @@ import {
|
|
|
45
45
|
export interface EmbedOptions {
|
|
46
46
|
/** Override config path */
|
|
47
47
|
configPath?: string;
|
|
48
|
+
/** Index name */
|
|
49
|
+
indexName?: string;
|
|
48
50
|
/** Restrict embedding work to a single collection */
|
|
49
51
|
collection?: string;
|
|
50
52
|
/** Override model URI */
|
|
@@ -417,6 +419,7 @@ interface EmbedContext {
|
|
|
417
419
|
*/
|
|
418
420
|
async function initEmbedContext(
|
|
419
421
|
configPath?: string,
|
|
422
|
+
indexName?: string,
|
|
420
423
|
collection?: string,
|
|
421
424
|
model?: string
|
|
422
425
|
): Promise<({ ok: true } & EmbedContext) | { ok: false; error: string }> {
|
|
@@ -440,9 +443,9 @@ async function initEmbedContext(
|
|
|
440
443
|
const modelUri = resolveModelUri(config, "embed", model, collection);
|
|
441
444
|
|
|
442
445
|
const store = new SqliteAdapter();
|
|
443
|
-
const dbPath = getIndexDbPath();
|
|
446
|
+
const dbPath = getIndexDbPath(indexName);
|
|
444
447
|
const paths = getConfigPaths();
|
|
445
|
-
store.setConfigPath(paths.configFile);
|
|
448
|
+
store.setConfigPath(configPath ?? paths.configFile);
|
|
446
449
|
|
|
447
450
|
const openResult = await store.open(dbPath, config.ftsTokenizer);
|
|
448
451
|
if (!openResult.ok) {
|
|
@@ -467,6 +470,7 @@ export async function embed(options: EmbedOptions = {}): Promise<EmbedResult> {
|
|
|
467
470
|
// Initialize config and store
|
|
468
471
|
const initResult = await initEmbedContext(
|
|
469
472
|
options.configPath,
|
|
473
|
+
options.indexName,
|
|
470
474
|
options.collection,
|
|
471
475
|
options.model
|
|
472
476
|
);
|
package/src/cli/commands/get.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import type { DocumentRow, StorePort, StoreResult } from "../../store/types";
|
|
9
9
|
import type { ParsedRef } from "./ref-parser";
|
|
10
10
|
|
|
11
|
+
import { decorateUriForIndex, parseUri } from "../../app/constants";
|
|
11
12
|
import {
|
|
12
13
|
getDocumentCapabilities,
|
|
13
14
|
type DocumentCapabilities,
|
|
@@ -22,6 +23,8 @@ import { initStore } from "./shared";
|
|
|
22
23
|
export interface GetCommandOptions {
|
|
23
24
|
/** Override config path */
|
|
24
25
|
configPath?: string;
|
|
26
|
+
/** Index name */
|
|
27
|
+
indexName?: string;
|
|
25
28
|
/** --from <line>, overrides :line suffix */
|
|
26
29
|
from?: number;
|
|
27
30
|
/** -l <lines> */
|
|
@@ -109,9 +112,13 @@ export async function get(
|
|
|
109
112
|
if ("error" in parsed) {
|
|
110
113
|
return { success: false, error: parsed.error, isValidation: true };
|
|
111
114
|
}
|
|
115
|
+
const explicitIndexName =
|
|
116
|
+
parsed.type === "uri" ? parseUri(parsed.value)?.indexName : undefined;
|
|
117
|
+
const indexName = explicitIndexName ?? options.indexName;
|
|
112
118
|
|
|
113
119
|
const initResult = await initStore({
|
|
114
120
|
configPath: options.configPath,
|
|
121
|
+
indexName,
|
|
115
122
|
syncConfig: false,
|
|
116
123
|
});
|
|
117
124
|
if (!initResult.ok) {
|
|
@@ -120,7 +127,7 @@ export async function get(
|
|
|
120
127
|
const { store, config } = initResult;
|
|
121
128
|
|
|
122
129
|
try {
|
|
123
|
-
return await fetchDocument(store, parsed, options, config);
|
|
130
|
+
return await fetchDocument(store, parsed, options, config, indexName);
|
|
124
131
|
} finally {
|
|
125
132
|
await store.close();
|
|
126
133
|
}
|
|
@@ -152,7 +159,8 @@ async function fetchDocument(
|
|
|
152
159
|
store: StorePort,
|
|
153
160
|
parsed: ParsedRef,
|
|
154
161
|
options: GetCommandOptions,
|
|
155
|
-
config: ConfigLike
|
|
162
|
+
config: ConfigLike,
|
|
163
|
+
indexName?: string
|
|
156
164
|
): Promise<GetResult> {
|
|
157
165
|
const docResult = await lookupDocument(store, parsed);
|
|
158
166
|
if (!docResult.ok) {
|
|
@@ -182,6 +190,7 @@ async function fetchDocument(
|
|
|
182
190
|
parsed,
|
|
183
191
|
options,
|
|
184
192
|
config,
|
|
193
|
+
indexName,
|
|
185
194
|
});
|
|
186
195
|
}
|
|
187
196
|
|
|
@@ -191,10 +200,11 @@ interface BuildResponseContext {
|
|
|
191
200
|
parsed: ParsedRef;
|
|
192
201
|
options: GetCommandOptions;
|
|
193
202
|
config: ConfigLike;
|
|
203
|
+
indexName?: string;
|
|
194
204
|
}
|
|
195
205
|
|
|
196
206
|
function buildResponse(ctx: BuildResponseContext): GetResult {
|
|
197
|
-
const { doc, fullContent, parsed, options, config } = ctx;
|
|
207
|
+
const { doc, fullContent, parsed, options, config, indexName } = ctx;
|
|
198
208
|
const lines = fullContent.split("\n");
|
|
199
209
|
const totalLines = lines.length;
|
|
200
210
|
|
|
@@ -204,7 +214,7 @@ function buildResponse(ctx: BuildResponseContext): GetResult {
|
|
|
204
214
|
success: true,
|
|
205
215
|
data: {
|
|
206
216
|
docid: doc.docid,
|
|
207
|
-
uri: doc.uri,
|
|
217
|
+
uri: decorateUriForIndex(doc.uri, indexName),
|
|
208
218
|
title: doc.title ?? undefined,
|
|
209
219
|
content: "",
|
|
210
220
|
totalLines,
|
|
@@ -232,7 +242,7 @@ function buildResponse(ctx: BuildResponseContext): GetResult {
|
|
|
232
242
|
success: true,
|
|
233
243
|
data: {
|
|
234
244
|
docid: doc.docid,
|
|
235
|
-
uri: doc.uri,
|
|
245
|
+
uri: decorateUriForIndex(doc.uri, indexName),
|
|
236
246
|
title: doc.title ?? undefined,
|
|
237
247
|
content,
|
|
238
248
|
totalLines,
|
|
@@ -14,6 +14,8 @@ import { formatSyncResultLines, initStore } from "./shared";
|
|
|
14
14
|
export interface IndexOptions {
|
|
15
15
|
/** Override config path */
|
|
16
16
|
configPath?: string;
|
|
17
|
+
/** Index name */
|
|
18
|
+
indexName?: string;
|
|
17
19
|
/** Scope to single collection */
|
|
18
20
|
collection?: string;
|
|
19
21
|
/** Run ingestion only, skip embedding */
|
|
@@ -46,6 +48,7 @@ export type IndexResult =
|
|
|
46
48
|
export async function index(options: IndexOptions = {}): Promise<IndexResult> {
|
|
47
49
|
const initResult = await initStore({
|
|
48
50
|
configPath: options.configPath,
|
|
51
|
+
indexName: options.indexName,
|
|
49
52
|
collection: options.collection,
|
|
50
53
|
});
|
|
51
54
|
if (!initResult.ok) {
|
|
@@ -71,6 +74,7 @@ export async function index(options: IndexOptions = {}): Promise<IndexResult> {
|
|
|
71
74
|
const { embed } = await import("./embed");
|
|
72
75
|
const result = await embed({
|
|
73
76
|
configPath: options.configPath,
|
|
77
|
+
indexName: options.indexName,
|
|
74
78
|
collection: options.collection,
|
|
75
79
|
verbose: options.verbose,
|
|
76
80
|
});
|
|
@@ -10,6 +10,7 @@ import { minimatch } from "minimatch";
|
|
|
10
10
|
import type { DocumentRow, StorePort, StoreResult } from "../../store/types";
|
|
11
11
|
import type { ParsedRef } from "./ref-parser";
|
|
12
12
|
|
|
13
|
+
import { decorateUriForIndex, parseUri } from "../../app/constants";
|
|
13
14
|
import { isGlobPattern, parseRef, splitRefs } from "./ref-parser";
|
|
14
15
|
import { initStore } from "./shared";
|
|
15
16
|
|
|
@@ -20,6 +21,8 @@ import { initStore } from "./shared";
|
|
|
20
21
|
export interface MultiGetCommandOptions {
|
|
21
22
|
/** Override config path */
|
|
22
23
|
configPath?: string;
|
|
24
|
+
/** Index name */
|
|
25
|
+
indexName?: string;
|
|
23
26
|
/** Max bytes per document (default 10240) */
|
|
24
27
|
maxBytes?: number;
|
|
25
28
|
/** Include line numbers */
|
|
@@ -170,6 +173,7 @@ function truncateContent(
|
|
|
170
173
|
interface FetchContext {
|
|
171
174
|
store: StorePort;
|
|
172
175
|
config: ConfigLike;
|
|
176
|
+
indexName?: string;
|
|
173
177
|
maxBytes: number;
|
|
174
178
|
documents: MultiGetDocument[];
|
|
175
179
|
skipped: SkippedDoc[];
|
|
@@ -222,7 +226,7 @@ async function fetchSingleDocument(
|
|
|
222
226
|
|
|
223
227
|
ctx.documents.push({
|
|
224
228
|
docid: doc.docid,
|
|
225
|
-
uri: doc.uri,
|
|
229
|
+
uri: decorateUriForIndex(doc.uri, ctx.indexName),
|
|
226
230
|
title: doc.title ?? undefined,
|
|
227
231
|
content,
|
|
228
232
|
truncated: truncated || undefined,
|
|
@@ -249,9 +253,18 @@ export async function multiGet(
|
|
|
249
253
|
): Promise<MultiGetResult> {
|
|
250
254
|
const maxBytes = options.maxBytes ?? 10_240;
|
|
251
255
|
const allRefs = splitRefs(refs);
|
|
256
|
+
const indexResolution = resolveMultiGetIndex(allRefs, options.indexName);
|
|
257
|
+
if (!indexResolution.ok) {
|
|
258
|
+
return {
|
|
259
|
+
success: false,
|
|
260
|
+
error: indexResolution.error,
|
|
261
|
+
isValidation: true,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
252
264
|
|
|
253
265
|
const initResult = await initStore({
|
|
254
266
|
configPath: options.configPath,
|
|
267
|
+
indexName: indexResolution.indexName,
|
|
255
268
|
syncConfig: false,
|
|
256
269
|
});
|
|
257
270
|
if (!initResult.ok) {
|
|
@@ -267,6 +280,7 @@ export async function multiGet(
|
|
|
267
280
|
const ctx: FetchContext = {
|
|
268
281
|
store,
|
|
269
282
|
config,
|
|
283
|
+
indexName: indexResolution.indexName,
|
|
270
284
|
maxBytes,
|
|
271
285
|
documents: [],
|
|
272
286
|
skipped: [],
|
|
@@ -301,6 +315,53 @@ export async function multiGet(
|
|
|
301
315
|
}
|
|
302
316
|
}
|
|
303
317
|
|
|
318
|
+
function resolveMultiGetIndex(
|
|
319
|
+
refs: string[],
|
|
320
|
+
globalIndexName?: string
|
|
321
|
+
): { ok: true; indexName?: string } | { ok: false; error: string } {
|
|
322
|
+
const explicitIndexes = new Set<string>();
|
|
323
|
+
let hasUnindexedRef = false;
|
|
324
|
+
|
|
325
|
+
for (const ref of refs) {
|
|
326
|
+
const parsed = parseRef(ref);
|
|
327
|
+
if ("error" in parsed || parsed.type !== "uri") {
|
|
328
|
+
hasUnindexedRef = true;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
const indexName = parseUri(parsed.value)?.indexName;
|
|
332
|
+
if (indexName) {
|
|
333
|
+
explicitIndexes.add(indexName);
|
|
334
|
+
} else {
|
|
335
|
+
hasUnindexedRef = true;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (explicitIndexes.size === 0) {
|
|
340
|
+
return { ok: true, indexName: globalIndexName };
|
|
341
|
+
}
|
|
342
|
+
if (explicitIndexes.size > 1) {
|
|
343
|
+
return {
|
|
344
|
+
ok: false,
|
|
345
|
+
error: `multi-get cannot mix explicit indexes: ${[...explicitIndexes].sort().join(", ")}`,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const explicitIndex = [...explicitIndexes][0];
|
|
350
|
+
if (
|
|
351
|
+
hasUnindexedRef &&
|
|
352
|
+
globalIndexName &&
|
|
353
|
+
explicitIndex &&
|
|
354
|
+
globalIndexName !== explicitIndex
|
|
355
|
+
) {
|
|
356
|
+
return {
|
|
357
|
+
ok: false,
|
|
358
|
+
error: `multi-get cannot mix indexed refs (${explicitIndex}) with unindexed refs while --index is ${globalIndexName}`,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return { ok: true, indexName: explicitIndex ?? globalIndexName };
|
|
363
|
+
}
|
|
364
|
+
|
|
304
365
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
305
366
|
// Formatter
|
|
306
367
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
createProgressRenderer,
|
|
26
26
|
createThrottledProgressRenderer,
|
|
27
27
|
} from "../progress";
|
|
28
|
-
import { initStore } from "./shared";
|
|
28
|
+
import { decorateSearchResultsForIndex, initStore } from "./shared";
|
|
29
29
|
|
|
30
30
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
31
31
|
// Types
|
|
@@ -34,6 +34,8 @@ import { initStore } from "./shared";
|
|
|
34
34
|
export type QueryCommandOptions = HybridSearchOptions & {
|
|
35
35
|
/** Override config path */
|
|
36
36
|
configPath?: string;
|
|
37
|
+
/** Index name */
|
|
38
|
+
indexName?: string;
|
|
37
39
|
/** Override embedding model */
|
|
38
40
|
embedModel?: string;
|
|
39
41
|
/** Override expansion model */
|
|
@@ -83,6 +85,7 @@ export async function query(
|
|
|
83
85
|
|
|
84
86
|
const initResult = await initStore({
|
|
85
87
|
configPath: options.configPath,
|
|
88
|
+
indexName: options.indexName,
|
|
86
89
|
collection: options.collection,
|
|
87
90
|
syncConfig: false,
|
|
88
91
|
});
|
|
@@ -208,7 +211,10 @@ export async function query(
|
|
|
208
211
|
return { success: false, error: result.error.message };
|
|
209
212
|
}
|
|
210
213
|
|
|
211
|
-
return {
|
|
214
|
+
return {
|
|
215
|
+
success: true,
|
|
216
|
+
data: decorateSearchResultsForIndex(result.value, options.indexName),
|
|
217
|
+
};
|
|
212
218
|
} finally {
|
|
213
219
|
if (embedPort) {
|
|
214
220
|
await embedPort.dispose();
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
type FormatOptions,
|
|
13
13
|
formatSearchResults,
|
|
14
14
|
} from "../format/search-results";
|
|
15
|
-
import { initStore } from "./shared";
|
|
15
|
+
import { decorateSearchResultsForIndex, initStore } from "./shared";
|
|
16
16
|
|
|
17
17
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
18
18
|
// Types
|
|
@@ -21,6 +21,8 @@ import { initStore } from "./shared";
|
|
|
21
21
|
export type SearchCommandOptions = SearchOptions & {
|
|
22
22
|
/** Override config path */
|
|
23
23
|
configPath?: string;
|
|
24
|
+
/** Index name */
|
|
25
|
+
indexName?: string;
|
|
24
26
|
/** Output as JSON */
|
|
25
27
|
json?: boolean;
|
|
26
28
|
/** Output as Markdown */
|
|
@@ -57,6 +59,7 @@ export async function search(
|
|
|
57
59
|
|
|
58
60
|
const initResult = await initStore({
|
|
59
61
|
configPath: options.configPath,
|
|
62
|
+
indexName: options.indexName,
|
|
60
63
|
collection: options.collection,
|
|
61
64
|
syncConfig: false,
|
|
62
65
|
});
|
|
@@ -83,7 +86,10 @@ export async function search(
|
|
|
83
86
|
};
|
|
84
87
|
}
|
|
85
88
|
|
|
86
|
-
return {
|
|
89
|
+
return {
|
|
90
|
+
success: true,
|
|
91
|
+
data: decorateSearchResultsForIndex(result.value, options.indexName),
|
|
92
|
+
};
|
|
87
93
|
} finally {
|
|
88
94
|
await store.close();
|
|
89
95
|
}
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
|
|
8
8
|
import type { Collection, Config } from "../../config/types";
|
|
9
9
|
import type { SyncResult } from "../../ingestion";
|
|
10
|
+
import type { SearchResults } from "../../pipeline/types";
|
|
10
11
|
|
|
11
|
-
import { getIndexDbPath } from "../../app/constants";
|
|
12
|
+
import { decorateUriForIndex, getIndexDbPath } from "../../app/constants";
|
|
12
13
|
import { getConfigPaths, isInitialized, loadConfig } from "../../config";
|
|
13
14
|
import { SqliteAdapter } from "../../store/sqlite/adapter";
|
|
14
15
|
|
|
@@ -122,6 +123,22 @@ export async function initStore(
|
|
|
122
123
|
return { ok: true, store, config, collections, actualConfigPath };
|
|
123
124
|
}
|
|
124
125
|
|
|
126
|
+
export function decorateSearchResultsForIndex(
|
|
127
|
+
data: SearchResults,
|
|
128
|
+
indexName?: string
|
|
129
|
+
): SearchResults {
|
|
130
|
+
if (!indexName) {
|
|
131
|
+
return data;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
...data,
|
|
135
|
+
results: data.results.map((result) => ({
|
|
136
|
+
...result,
|
|
137
|
+
uri: decorateUriForIndex(result.uri, indexName),
|
|
138
|
+
})),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
125
142
|
/**
|
|
126
143
|
* Format sync result lines (shared between update and index commands).
|
|
127
144
|
*/
|
|
@@ -18,6 +18,8 @@ import { SqliteAdapter } from "../../store/sqlite/adapter";
|
|
|
18
18
|
export interface StatusOptions {
|
|
19
19
|
/** Override config path */
|
|
20
20
|
configPath?: string;
|
|
21
|
+
/** Index name */
|
|
22
|
+
indexName?: string;
|
|
21
23
|
/** Output as JSON */
|
|
22
24
|
json?: boolean;
|
|
23
25
|
/** Output as Markdown */
|
|
@@ -137,11 +139,11 @@ export async function status(
|
|
|
137
139
|
|
|
138
140
|
// Open database
|
|
139
141
|
const store = new SqliteAdapter();
|
|
140
|
-
const dbPath = getIndexDbPath();
|
|
142
|
+
const dbPath = getIndexDbPath(options.indexName);
|
|
141
143
|
const paths = getConfigPaths();
|
|
142
144
|
|
|
143
145
|
// Set configPath for status output
|
|
144
|
-
store.setConfigPath(paths.configFile);
|
|
146
|
+
store.setConfigPath(options.configPath ?? paths.configFile);
|
|
145
147
|
|
|
146
148
|
const openResult = await store.open(dbPath, config.ftsTokenizer);
|
|
147
149
|
if (!openResult.ok) {
|
|
@@ -14,6 +14,8 @@ import { formatSyncResultLines, initStore } from "./shared";
|
|
|
14
14
|
export interface UpdateOptions {
|
|
15
15
|
/** Override config path */
|
|
16
16
|
configPath?: string;
|
|
17
|
+
/** Index name */
|
|
18
|
+
indexName?: string;
|
|
17
19
|
/** Run git pull in git repositories before scanning */
|
|
18
20
|
gitPull?: boolean;
|
|
19
21
|
/** Verbose output */
|
|
@@ -33,7 +35,10 @@ export type UpdateResult =
|
|
|
33
35
|
export async function update(
|
|
34
36
|
options: UpdateOptions = {}
|
|
35
37
|
): Promise<UpdateResult> {
|
|
36
|
-
const initResult = await initStore({
|
|
38
|
+
const initResult = await initStore({
|
|
39
|
+
configPath: options.configPath,
|
|
40
|
+
indexName: options.indexName,
|
|
41
|
+
});
|
|
37
42
|
if (!initResult.ok) {
|
|
38
43
|
return { success: false, error: initResult.error };
|
|
39
44
|
}
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
type FormatOptions,
|
|
20
20
|
formatSearchResults,
|
|
21
21
|
} from "../format/search-results";
|
|
22
|
-
import { initStore } from "./shared";
|
|
22
|
+
import { decorateSearchResultsForIndex, initStore } from "./shared";
|
|
23
23
|
|
|
24
24
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
25
25
|
// Types
|
|
@@ -28,6 +28,8 @@ import { initStore } from "./shared";
|
|
|
28
28
|
export type VsearchCommandOptions = SearchOptions & {
|
|
29
29
|
/** Override config path */
|
|
30
30
|
configPath?: string;
|
|
31
|
+
/** Index name */
|
|
32
|
+
indexName?: string;
|
|
31
33
|
/** Override model URI */
|
|
32
34
|
model?: string;
|
|
33
35
|
/** Output as JSON */
|
|
@@ -66,6 +68,7 @@ export async function vsearch(
|
|
|
66
68
|
|
|
67
69
|
const initResult = await initStore({
|
|
68
70
|
configPath: options.configPath,
|
|
71
|
+
indexName: options.indexName,
|
|
69
72
|
collection: options.collection,
|
|
70
73
|
syncConfig: false,
|
|
71
74
|
});
|
|
@@ -137,7 +140,10 @@ export async function vsearch(
|
|
|
137
140
|
return { success: false, error: result.error.message };
|
|
138
141
|
}
|
|
139
142
|
|
|
140
|
-
return {
|
|
143
|
+
return {
|
|
144
|
+
success: true,
|
|
145
|
+
data: decorateSearchResultsForIndex(result.value, options.indexName),
|
|
146
|
+
};
|
|
141
147
|
} finally {
|
|
142
148
|
await embedPort.dispose();
|
|
143
149
|
}
|