context-mode 1.0.98 → 1.0.100
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/README.md +9 -7
- package/build/adapters/claude-code-base.js +4 -4
- package/build/adapters/codex/index.js +23 -1
- package/build/adapters/qwen-code/index.d.ts +1 -1
- package/build/adapters/qwen-code/index.js +110 -4
- package/build/cli.js +2 -0
- package/build/opencode-plugin.js +1 -1
- package/build/pi-extension.js +1 -1
- package/build/search/auto-memory.d.ts +29 -0
- package/build/search/auto-memory.js +121 -0
- package/build/search/unified.d.ts +41 -0
- package/build/search/unified.js +89 -0
- package/build/server.js +88 -40
- package/build/session/analytics.js +1 -1
- package/build/session/db.d.ts +17 -0
- package/build/session/db.js +28 -0
- package/build/session/extract.d.ts +4 -0
- package/build/session/extract.js +232 -1
- package/build/session/snapshot.js +31 -0
- package/build/store.js +118 -8
- package/build/types.d.ts +1 -0
- package/cli.bundle.mjs +260 -125
- package/configs/claude-code/CLAUDE.md +21 -1
- package/configs/codex/AGENTS.md +23 -2
- package/configs/codex/hooks.json +14 -0
- package/configs/cursor/context-mode.mdc +18 -1
- package/configs/gemini-cli/GEMINI.md +22 -1
- package/configs/jetbrains-copilot/copilot-instructions.md +22 -1
- package/configs/kilo/AGENTS.md +19 -2
- package/configs/kiro/KIRO.md +18 -1
- package/configs/openclaw/AGENTS.md +22 -2
- package/configs/opencode/AGENTS.md +18 -1
- package/configs/pi/AGENTS.md +18 -1
- package/configs/qwen-code/QWEN.md +38 -18
- package/configs/vscode-copilot/copilot-instructions.md +22 -1
- package/hooks/auto-injection.mjs +76 -0
- package/hooks/codex/stop.mjs +43 -0
- package/hooks/codex/userpromptsubmit.mjs +75 -0
- package/hooks/core/mcp-ready.mjs +7 -1
- package/hooks/posttooluse.mjs +50 -1
- package/hooks/precompact.mjs +9 -0
- package/hooks/pretooluse.mjs +27 -0
- package/hooks/routing-block.mjs +7 -1
- package/hooks/session-db.bundle.mjs +19 -13
- package/hooks/session-extract.bundle.mjs +2 -2
- package/hooks/session-snapshot.bundle.mjs +18 -17
- package/hooks/sessionstart.mjs +17 -0
- package/hooks/userpromptsubmit.mjs +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +228 -93
- package/skills/context-mode-ops/agent-teams.md +1 -1
|
@@ -312,6 +312,30 @@ function buildSkillsSection(skillEvents, searchTool) {
|
|
|
312
312
|
];
|
|
313
313
|
return lines.join("\n");
|
|
314
314
|
}
|
|
315
|
+
function buildRolesSection(roleEvents, searchTool) {
|
|
316
|
+
if (roleEvents.length === 0)
|
|
317
|
+
return "";
|
|
318
|
+
const seen = new Set();
|
|
319
|
+
const summaryLines = [];
|
|
320
|
+
const queryTerms = [];
|
|
321
|
+
for (const ev of roleEvents) {
|
|
322
|
+
if (seen.has(ev.data))
|
|
323
|
+
continue;
|
|
324
|
+
seen.add(ev.data);
|
|
325
|
+
summaryLines.push(` ${escapeXML(ev.data)}`);
|
|
326
|
+
queryTerms.push(ev.data);
|
|
327
|
+
}
|
|
328
|
+
if (summaryLines.length === 0)
|
|
329
|
+
return "";
|
|
330
|
+
const queries = buildQueries(queryTerms);
|
|
331
|
+
const lines = [
|
|
332
|
+
` <roles count="${summaryLines.length}">`,
|
|
333
|
+
...summaryLines,
|
|
334
|
+
toolCall(searchTool, queries),
|
|
335
|
+
` </roles>`,
|
|
336
|
+
];
|
|
337
|
+
return lines.join("\n");
|
|
338
|
+
}
|
|
315
339
|
function buildIntentSection(intentEvents) {
|
|
316
340
|
if (intentEvents.length === 0)
|
|
317
341
|
return "";
|
|
@@ -344,6 +368,7 @@ export function buildResumeSnapshot(events, opts) {
|
|
|
344
368
|
const subagentEvents = [];
|
|
345
369
|
const intentEvents = [];
|
|
346
370
|
const skillEvents = [];
|
|
371
|
+
const roleEvents = [];
|
|
347
372
|
for (const ev of events) {
|
|
348
373
|
switch (ev.category) {
|
|
349
374
|
case "file":
|
|
@@ -379,6 +404,9 @@ export function buildResumeSnapshot(events, opts) {
|
|
|
379
404
|
case "skill":
|
|
380
405
|
skillEvents.push(ev);
|
|
381
406
|
break;
|
|
407
|
+
case "role":
|
|
408
|
+
roleEvents.push(ev);
|
|
409
|
+
break;
|
|
382
410
|
}
|
|
383
411
|
}
|
|
384
412
|
// ── Build all sections ──
|
|
@@ -417,6 +445,9 @@ export function buildResumeSnapshot(events, opts) {
|
|
|
417
445
|
const skills = buildSkillsSection(skillEvents, searchTool);
|
|
418
446
|
if (skills)
|
|
419
447
|
sections.push(skills);
|
|
448
|
+
const roles = buildRolesSection(roleEvents, searchTool);
|
|
449
|
+
if (roles)
|
|
450
|
+
sections.push(roles);
|
|
420
451
|
const intent = buildIntentSection(intentEvents);
|
|
421
452
|
if (intent)
|
|
422
453
|
sections.push(intent);
|
package/build/store.js
CHANGED
|
@@ -218,6 +218,40 @@ function findAllPositions(text, term) {
|
|
|
218
218
|
}
|
|
219
219
|
return positions;
|
|
220
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* Count matched adjacent pairs across consecutive query terms.
|
|
223
|
+
* For each pair (term[i], term[i+1]), pairs each left position with at most one
|
|
224
|
+
* right position whose offset falls within `gap` chars of `p + len(term[i])`.
|
|
225
|
+
* `positionLists` must be sorted ascending (output of `findAllPositions` is).
|
|
226
|
+
* Each right position is consumed by at most one left, so `"foo foo bar"`
|
|
227
|
+
* counts 1 pair, not 2 — matches IR phrase-occurrence intent and avoids
|
|
228
|
+
* inflating boosts for repeated-token queries.
|
|
229
|
+
* Used by reranker to layer a frequency signal on top of minSpan proximity:
|
|
230
|
+
* 30-char gap covers natural prose without rewarding distant matches.
|
|
231
|
+
*/
|
|
232
|
+
function countAdjacentPairs(positionLists, terms, gap = 30) {
|
|
233
|
+
if (positionLists.length < 2 || terms.length < 2)
|
|
234
|
+
return 0;
|
|
235
|
+
let total = 0;
|
|
236
|
+
const pairs = Math.min(positionLists.length, terms.length) - 1;
|
|
237
|
+
for (let i = 0; i < pairs; i++) {
|
|
238
|
+
const left = positionLists[i];
|
|
239
|
+
const right = positionLists[i + 1];
|
|
240
|
+
const leftLen = terms[i].length;
|
|
241
|
+
let j = 0;
|
|
242
|
+
for (const p of left) {
|
|
243
|
+
const minStart = p + leftLen;
|
|
244
|
+
const maxStart = minStart + gap;
|
|
245
|
+
while (j < right.length && right[j] < minStart)
|
|
246
|
+
j++;
|
|
247
|
+
if (j < right.length && right[j] <= maxStart) {
|
|
248
|
+
total++;
|
|
249
|
+
j++;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return total;
|
|
254
|
+
}
|
|
221
255
|
/**
|
|
222
256
|
* Find minimum span (window) covering at least one position from each list.
|
|
223
257
|
* Uses a sweep-line approach: advance the pointer at the current minimum.
|
|
@@ -368,6 +402,10 @@ export class ContentStore {
|
|
|
368
402
|
content,
|
|
369
403
|
source_id UNINDEXED,
|
|
370
404
|
content_type UNINDEXED,
|
|
405
|
+
source_category UNINDEXED,
|
|
406
|
+
session_id UNINDEXED,
|
|
407
|
+
event_id UNINDEXED,
|
|
408
|
+
timestamp UNINDEXED,
|
|
371
409
|
tokenize='porter unicode61'
|
|
372
410
|
);
|
|
373
411
|
|
|
@@ -376,6 +414,10 @@ export class ContentStore {
|
|
|
376
414
|
content,
|
|
377
415
|
source_id UNINDEXED,
|
|
378
416
|
content_type UNINDEXED,
|
|
417
|
+
source_category UNINDEXED,
|
|
418
|
+
session_id UNINDEXED,
|
|
419
|
+
event_id UNINDEXED,
|
|
420
|
+
timestamp UNINDEXED,
|
|
379
421
|
tokenize='trigram'
|
|
380
422
|
);
|
|
381
423
|
|
|
@@ -385,6 +427,47 @@ export class ContentStore {
|
|
|
385
427
|
|
|
386
428
|
CREATE INDEX IF NOT EXISTS idx_sources_label ON sources(label);
|
|
387
429
|
`);
|
|
430
|
+
// FTS5 schema migration: old schema (4 cols) → new schema (8 cols).
|
|
431
|
+
// FTS5 virtual tables do not support ALTER TABLE ADD COLUMN, so we must
|
|
432
|
+
// DROP + re-CREATE. Detection: check for sentinel column `source_category`
|
|
433
|
+
// via pragma_table_xinfo. Three states:
|
|
434
|
+
// 1. No table → CREATE above handled it (fresh DB)
|
|
435
|
+
// 2. Old schema (4 cols) → DROP + CREATE new
|
|
436
|
+
// 3. New schema (8 cols) → do nothing
|
|
437
|
+
try {
|
|
438
|
+
const cols = this.#db.prepare("SELECT name FROM pragma_table_xinfo('chunks')").all();
|
|
439
|
+
const colNames = new Set(cols.map(c => c.name));
|
|
440
|
+
if (cols.length > 0 && !colNames.has("source_category")) {
|
|
441
|
+
// Old schema detected — drop both FTS5 tables and re-create with new columns
|
|
442
|
+
this.#db.exec("DROP TABLE IF EXISTS chunks");
|
|
443
|
+
this.#db.exec("DROP TABLE IF EXISTS chunks_trigram");
|
|
444
|
+
this.#db.exec(`
|
|
445
|
+
CREATE VIRTUAL TABLE chunks USING fts5(
|
|
446
|
+
title,
|
|
447
|
+
content,
|
|
448
|
+
source_id UNINDEXED,
|
|
449
|
+
content_type UNINDEXED,
|
|
450
|
+
source_category UNINDEXED,
|
|
451
|
+
session_id UNINDEXED,
|
|
452
|
+
event_id UNINDEXED,
|
|
453
|
+
timestamp UNINDEXED,
|
|
454
|
+
tokenize='porter unicode61'
|
|
455
|
+
);
|
|
456
|
+
CREATE VIRTUAL TABLE chunks_trigram USING fts5(
|
|
457
|
+
title,
|
|
458
|
+
content,
|
|
459
|
+
source_id UNINDEXED,
|
|
460
|
+
content_type UNINDEXED,
|
|
461
|
+
source_category UNINDEXED,
|
|
462
|
+
session_id UNINDEXED,
|
|
463
|
+
event_id UNINDEXED,
|
|
464
|
+
timestamp UNINDEXED,
|
|
465
|
+
tokenize='trigram'
|
|
466
|
+
);
|
|
467
|
+
`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
catch { /* pragma_table_xinfo may fail if table doesn't exist yet — safe to ignore */ }
|
|
388
471
|
// Stale detection columns — safe for existing DBs (ALTER is O(1) in SQLite)
|
|
389
472
|
try {
|
|
390
473
|
this.#db.exec("ALTER TABLE sources ADD COLUMN file_path TEXT");
|
|
@@ -399,8 +482,8 @@ export class ContentStore {
|
|
|
399
482
|
// Write path
|
|
400
483
|
this.#stmtInsertSourceEmpty = this.#db.prepare("INSERT INTO sources (label, chunk_count, code_chunk_count, file_path, content_hash) VALUES (?, 0, 0, ?, ?)");
|
|
401
484
|
this.#stmtInsertSource = this.#db.prepare("INSERT INTO sources (label, chunk_count, code_chunk_count, file_path, content_hash) VALUES (?, ?, ?, ?, ?)");
|
|
402
|
-
this.#stmtInsertChunk = this.#db.prepare("INSERT INTO chunks (title, content, source_id, content_type) VALUES (?, ?, ?, ?)");
|
|
403
|
-
this.#stmtInsertChunkTrigram = this.#db.prepare("INSERT INTO chunks_trigram (title, content, source_id, content_type) VALUES (?, ?, ?, ?)");
|
|
485
|
+
this.#stmtInsertChunk = this.#db.prepare("INSERT INTO chunks (title, content, source_id, content_type, source_category, session_id, event_id, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
|
486
|
+
this.#stmtInsertChunkTrigram = this.#db.prepare("INSERT INTO chunks_trigram (title, content, source_id, content_type, source_category, session_id, event_id, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
|
404
487
|
this.#stmtInsertVocab = this.#db.prepare("INSERT OR IGNORE INTO vocabulary (word) VALUES (?)");
|
|
405
488
|
// Dedup path: delete previous source with same label before re-indexing
|
|
406
489
|
// Prevents stale outputs from accumulating in iterative workflows (build-fix-build)
|
|
@@ -413,6 +496,7 @@ export class ContentStore {
|
|
|
413
496
|
chunks.title,
|
|
414
497
|
chunks.content,
|
|
415
498
|
chunks.content_type,
|
|
499
|
+
chunks.timestamp,
|
|
416
500
|
sources.label,
|
|
417
501
|
bm25(chunks, 5.0, 1.0) AS rank,
|
|
418
502
|
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
@@ -427,6 +511,7 @@ export class ContentStore {
|
|
|
427
511
|
chunks.title,
|
|
428
512
|
chunks.content,
|
|
429
513
|
chunks.content_type,
|
|
514
|
+
chunks.timestamp,
|
|
430
515
|
sources.label,
|
|
431
516
|
bm25(chunks, 5.0, 1.0) AS rank,
|
|
432
517
|
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
@@ -441,6 +526,7 @@ export class ContentStore {
|
|
|
441
526
|
chunks.title,
|
|
442
527
|
chunks.content,
|
|
443
528
|
chunks.content_type,
|
|
529
|
+
chunks.timestamp,
|
|
444
530
|
sources.label,
|
|
445
531
|
bm25(chunks, 5.0, 1.0) AS rank,
|
|
446
532
|
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
@@ -455,6 +541,7 @@ export class ContentStore {
|
|
|
455
541
|
chunks_trigram.title,
|
|
456
542
|
chunks_trigram.content,
|
|
457
543
|
chunks_trigram.content_type,
|
|
544
|
+
chunks_trigram.timestamp,
|
|
458
545
|
sources.label,
|
|
459
546
|
bm25(chunks_trigram, 5.0, 1.0) AS rank,
|
|
460
547
|
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
@@ -469,6 +556,7 @@ export class ContentStore {
|
|
|
469
556
|
chunks_trigram.title,
|
|
470
557
|
chunks_trigram.content,
|
|
471
558
|
chunks_trigram.content_type,
|
|
559
|
+
chunks_trigram.timestamp,
|
|
472
560
|
sources.label,
|
|
473
561
|
bm25(chunks_trigram, 5.0, 1.0) AS rank,
|
|
474
562
|
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
@@ -483,6 +571,7 @@ export class ContentStore {
|
|
|
483
571
|
chunks_trigram.title,
|
|
484
572
|
chunks_trigram.content,
|
|
485
573
|
chunks_trigram.content_type,
|
|
574
|
+
chunks_trigram.timestamp,
|
|
486
575
|
sources.label,
|
|
487
576
|
bm25(chunks_trigram, 5.0, 1.0) AS rank,
|
|
488
577
|
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
@@ -498,6 +587,7 @@ export class ContentStore {
|
|
|
498
587
|
chunks.title,
|
|
499
588
|
chunks.content,
|
|
500
589
|
chunks.content_type,
|
|
590
|
+
chunks.timestamp,
|
|
501
591
|
sources.label,
|
|
502
592
|
bm25(chunks, 5.0, 1.0) AS rank,
|
|
503
593
|
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
@@ -512,6 +602,7 @@ export class ContentStore {
|
|
|
512
602
|
chunks.title,
|
|
513
603
|
chunks.content,
|
|
514
604
|
chunks.content_type,
|
|
605
|
+
chunks.timestamp,
|
|
515
606
|
sources.label,
|
|
516
607
|
bm25(chunks, 5.0, 1.0) AS rank,
|
|
517
608
|
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
@@ -526,6 +617,7 @@ export class ContentStore {
|
|
|
526
617
|
chunks.title,
|
|
527
618
|
chunks.content,
|
|
528
619
|
chunks.content_type,
|
|
620
|
+
chunks.timestamp,
|
|
529
621
|
sources.label,
|
|
530
622
|
bm25(chunks, 5.0, 1.0) AS rank,
|
|
531
623
|
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
@@ -540,6 +632,7 @@ export class ContentStore {
|
|
|
540
632
|
chunks_trigram.title,
|
|
541
633
|
chunks_trigram.content,
|
|
542
634
|
chunks_trigram.content_type,
|
|
635
|
+
chunks_trigram.timestamp,
|
|
543
636
|
sources.label,
|
|
544
637
|
bm25(chunks_trigram, 5.0, 1.0) AS rank,
|
|
545
638
|
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
@@ -554,6 +647,7 @@ export class ContentStore {
|
|
|
554
647
|
chunks_trigram.title,
|
|
555
648
|
chunks_trigram.content,
|
|
556
649
|
chunks_trigram.content_type,
|
|
650
|
+
chunks_trigram.timestamp,
|
|
557
651
|
sources.label,
|
|
558
652
|
bm25(chunks_trigram, 5.0, 1.0) AS rank,
|
|
559
653
|
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
@@ -568,6 +662,7 @@ export class ContentStore {
|
|
|
568
662
|
chunks_trigram.title,
|
|
569
663
|
chunks_trigram.content,
|
|
570
664
|
chunks_trigram.content_type,
|
|
665
|
+
chunks_trigram.timestamp,
|
|
571
666
|
sources.label,
|
|
572
667
|
bm25(chunks_trigram, 5.0, 1.0) AS rank,
|
|
573
668
|
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
@@ -603,10 +698,16 @@ export class ContentStore {
|
|
|
603
698
|
// ── Index ──
|
|
604
699
|
index(options) {
|
|
605
700
|
const { content, path, source } = options;
|
|
606
|
-
|
|
701
|
+
// Treat empty string as "no content" so an empty `content` paired with a
|
|
702
|
+
// valid `path` falls back to reading the file. Some MCP clients
|
|
703
|
+
// materialize optional string fields as `""` and the previous
|
|
704
|
+
// `content ?? readFileSync(path)` kept the empty string, indexing 0
|
|
705
|
+
// chunks. See issue #350.
|
|
706
|
+
const hasContent = typeof content === "string" && content.length > 0;
|
|
707
|
+
if (!hasContent && !path) {
|
|
607
708
|
throw new Error("Either content or path must be provided");
|
|
608
709
|
}
|
|
609
|
-
const text = content
|
|
710
|
+
const text = hasContent ? content : readFileSync(path, "utf-8");
|
|
610
711
|
const label = source ?? path ?? "untitled";
|
|
611
712
|
const chunks = this.#chunkMarkdown(text);
|
|
612
713
|
// Stale detection: store file_path + SHA-256 for file-backed sources
|
|
@@ -674,10 +775,11 @@ export class ContentStore {
|
|
|
674
775
|
}
|
|
675
776
|
const info = this.#stmtInsertSource.run(label, chunks.length, codeChunks, filePath ?? null, contentHash ?? null);
|
|
676
777
|
const sourceId = Number(info.lastInsertRowid);
|
|
778
|
+
const now = new Date().toISOString();
|
|
677
779
|
for (const chunk of chunks) {
|
|
678
780
|
const ct = chunk.hasCode ? "code" : "prose";
|
|
679
|
-
this.#stmtInsertChunk.run(chunk.title, chunk.content, sourceId, ct);
|
|
680
|
-
this.#stmtInsertChunkTrigram.run(chunk.title, chunk.content, sourceId, ct);
|
|
781
|
+
this.#stmtInsertChunk.run(chunk.title, chunk.content, sourceId, ct, null, null, null, now);
|
|
782
|
+
this.#stmtInsertChunkTrigram.run(chunk.title, chunk.content, sourceId, ct, null, null, null, now);
|
|
681
783
|
}
|
|
682
784
|
return sourceId;
|
|
683
785
|
});
|
|
@@ -708,6 +810,7 @@ export class ContentStore {
|
|
|
708
810
|
rank: r.rank,
|
|
709
811
|
contentType: r.content_type,
|
|
710
812
|
highlighted: r.highlighted,
|
|
813
|
+
timestamp: r.timestamp ?? undefined,
|
|
711
814
|
}));
|
|
712
815
|
}
|
|
713
816
|
#sourceFilterParam(source, sourceMatchMode) {
|
|
@@ -859,17 +962,24 @@ export class ContentStore {
|
|
|
859
962
|
const titleHits = terms.filter((t) => titleLower.includes(t)).length;
|
|
860
963
|
const titleWeight = r.contentType === "code" ? 0.6 : 0.3;
|
|
861
964
|
const titleBoost = titleHits > 0 ? titleWeight * (titleHits / terms.length) : 0;
|
|
862
|
-
// Proximity boost for multi-term queries
|
|
965
|
+
// Proximity boost for multi-term queries. minSpan picks the single
|
|
966
|
+
// tightest window — frequency doesn't move it, so a long doc with one
|
|
967
|
+
// tight occurrence outranks a short doc with several. Phrase-frequency
|
|
968
|
+
// reward layers a saturating frequency signal on top: cap 0.5 (below
|
|
969
|
+
// proximity max ≈1.0, in title-boost range), saturates at 4 hits.
|
|
863
970
|
let proximityBoost = 0;
|
|
971
|
+
let phraseBoost = 0;
|
|
864
972
|
if (terms.length >= 2) {
|
|
865
973
|
const content = r.content.toLowerCase();
|
|
866
974
|
const positions = terms.map((t) => findAllPositions(content, t));
|
|
867
975
|
if (!positions.some((p) => p.length === 0)) {
|
|
868
976
|
const minSpan = findMinSpan(positions);
|
|
869
977
|
proximityBoost = 1 / (1 + minSpan / Math.max(content.length, 1));
|
|
978
|
+
const adjacentPairs = countAdjacentPairs(positions, terms);
|
|
979
|
+
phraseBoost = 0.5 * Math.min(1, adjacentPairs / 4);
|
|
870
980
|
}
|
|
871
981
|
}
|
|
872
|
-
return { result: r, boost: titleBoost + proximityBoost };
|
|
982
|
+
return { result: r, boost: titleBoost + proximityBoost + phraseBoost };
|
|
873
983
|
})
|
|
874
984
|
.sort((a, b) => b.boost - a.boost || a.result.rank - b.result.rank)
|
|
875
985
|
.map(({ result }) => result);
|
package/build/types.d.ts
CHANGED