context-mode 1.0.52 → 1.0.54
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 +87 -29
- package/build/adapters/claude-code/hooks.d.ts +18 -0
- package/build/adapters/claude-code/hooks.js +23 -0
- package/build/adapters/claude-code/index.js +34 -2
- package/build/adapters/client-map.js +1 -0
- package/build/adapters/detect.d.ts +1 -0
- package/build/adapters/detect.js +18 -2
- package/build/adapters/opencode/index.d.ts +7 -2
- package/build/adapters/opencode/index.js +37 -24
- package/build/adapters/types.d.ts +1 -1
- package/build/cli.js +12 -28
- package/build/executor.js +3 -3
- package/build/openclaw-plugin.js +41 -33
- package/build/opencode-plugin.js +5 -2
- package/build/runtime.js +29 -11
- package/build/server.d.ts +2 -0
- package/build/server.js +35 -44
- package/build/store.d.ts +4 -3
- package/build/store.js +101 -34
- package/cli.bundle.mjs +185 -131
- package/configs/codex/AGENTS.md +19 -0
- package/configs/kilo/AGENTS.md +58 -0
- package/configs/kilo/kilo.json +10 -0
- package/hooks/core/tool-naming.mjs +1 -0
- package/hooks/pretooluse.mjs +25 -20
- package/hooks/sessionstart.mjs +25 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +156 -102
package/build/store.js
CHANGED
|
@@ -212,13 +212,17 @@ export class ContentStore {
|
|
|
212
212
|
// Search path (hot)
|
|
213
213
|
#stmtSearchPorter;
|
|
214
214
|
#stmtSearchPorterFiltered;
|
|
215
|
+
#stmtSearchPorterExact;
|
|
215
216
|
#stmtSearchTrigram;
|
|
216
217
|
#stmtSearchTrigramFiltered;
|
|
218
|
+
#stmtSearchTrigramExact;
|
|
217
219
|
#stmtFuzzyVocab;
|
|
218
220
|
#stmtSearchPorterContentType;
|
|
219
221
|
#stmtSearchPorterFilteredContentType;
|
|
222
|
+
#stmtSearchPorterExactContentType;
|
|
220
223
|
#stmtSearchTrigramContentType;
|
|
221
224
|
#stmtSearchTrigramFilteredContentType;
|
|
225
|
+
#stmtSearchTrigramExactContentType;
|
|
222
226
|
// Read path
|
|
223
227
|
#stmtListSources;
|
|
224
228
|
#stmtChunksBySource;
|
|
@@ -278,6 +282,8 @@ export class ContentStore {
|
|
|
278
282
|
CREATE TABLE IF NOT EXISTS vocabulary (
|
|
279
283
|
word TEXT PRIMARY KEY
|
|
280
284
|
);
|
|
285
|
+
|
|
286
|
+
CREATE INDEX IF NOT EXISTS idx_sources_label ON sources(label);
|
|
281
287
|
`);
|
|
282
288
|
}
|
|
283
289
|
#prepareStatements() {
|
|
@@ -320,6 +326,20 @@ export class ContentStore {
|
|
|
320
326
|
WHERE chunks MATCH ? AND sources.label LIKE ?
|
|
321
327
|
ORDER BY rank
|
|
322
328
|
LIMIT ?
|
|
329
|
+
`);
|
|
330
|
+
this.#stmtSearchPorterExact = this.#db.prepare(`
|
|
331
|
+
SELECT
|
|
332
|
+
chunks.title,
|
|
333
|
+
chunks.content,
|
|
334
|
+
chunks.content_type,
|
|
335
|
+
sources.label,
|
|
336
|
+
bm25(chunks, 5.0, 1.0) AS rank,
|
|
337
|
+
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
338
|
+
FROM chunks
|
|
339
|
+
JOIN sources ON sources.id = chunks.source_id
|
|
340
|
+
WHERE chunks MATCH ? AND sources.label = ?
|
|
341
|
+
ORDER BY rank
|
|
342
|
+
LIMIT ?
|
|
323
343
|
`);
|
|
324
344
|
this.#stmtSearchTrigram = this.#db.prepare(`
|
|
325
345
|
SELECT
|
|
@@ -348,6 +368,20 @@ export class ContentStore {
|
|
|
348
368
|
WHERE chunks_trigram MATCH ? AND sources.label LIKE ?
|
|
349
369
|
ORDER BY rank
|
|
350
370
|
LIMIT ?
|
|
371
|
+
`);
|
|
372
|
+
this.#stmtSearchTrigramExact = this.#db.prepare(`
|
|
373
|
+
SELECT
|
|
374
|
+
chunks_trigram.title,
|
|
375
|
+
chunks_trigram.content,
|
|
376
|
+
chunks_trigram.content_type,
|
|
377
|
+
sources.label,
|
|
378
|
+
bm25(chunks_trigram, 5.0, 1.0) AS rank,
|
|
379
|
+
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
380
|
+
FROM chunks_trigram
|
|
381
|
+
JOIN sources ON sources.id = chunks_trigram.source_id
|
|
382
|
+
WHERE chunks_trigram MATCH ? AND sources.label = ?
|
|
383
|
+
ORDER BY rank
|
|
384
|
+
LIMIT ?
|
|
351
385
|
`);
|
|
352
386
|
// Content-type filtered variants
|
|
353
387
|
this.#stmtSearchPorterContentType = this.#db.prepare(`
|
|
@@ -377,6 +411,20 @@ export class ContentStore {
|
|
|
377
411
|
WHERE chunks MATCH ? AND sources.label LIKE ? AND chunks.content_type = ?
|
|
378
412
|
ORDER BY rank
|
|
379
413
|
LIMIT ?
|
|
414
|
+
`);
|
|
415
|
+
this.#stmtSearchPorterExactContentType = this.#db.prepare(`
|
|
416
|
+
SELECT
|
|
417
|
+
chunks.title,
|
|
418
|
+
chunks.content,
|
|
419
|
+
chunks.content_type,
|
|
420
|
+
sources.label,
|
|
421
|
+
bm25(chunks, 5.0, 1.0) AS rank,
|
|
422
|
+
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
423
|
+
FROM chunks
|
|
424
|
+
JOIN sources ON sources.id = chunks.source_id
|
|
425
|
+
WHERE chunks MATCH ? AND sources.label = ? AND chunks.content_type = ?
|
|
426
|
+
ORDER BY rank
|
|
427
|
+
LIMIT ?
|
|
380
428
|
`);
|
|
381
429
|
this.#stmtSearchTrigramContentType = this.#db.prepare(`
|
|
382
430
|
SELECT
|
|
@@ -405,6 +453,20 @@ export class ContentStore {
|
|
|
405
453
|
WHERE chunks_trigram MATCH ? AND sources.label LIKE ? AND chunks_trigram.content_type = ?
|
|
406
454
|
ORDER BY rank
|
|
407
455
|
LIMIT ?
|
|
456
|
+
`);
|
|
457
|
+
this.#stmtSearchTrigramExactContentType = this.#db.prepare(`
|
|
458
|
+
SELECT
|
|
459
|
+
chunks_trigram.title,
|
|
460
|
+
chunks_trigram.content,
|
|
461
|
+
chunks_trigram.content_type,
|
|
462
|
+
sources.label,
|
|
463
|
+
bm25(chunks_trigram, 5.0, 1.0) AS rank,
|
|
464
|
+
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
465
|
+
FROM chunks_trigram
|
|
466
|
+
JOIN sources ON sources.id = chunks_trigram.source_id
|
|
467
|
+
WHERE chunks_trigram MATCH ? AND sources.label = ? AND chunks_trigram.content_type = ?
|
|
468
|
+
ORDER BY rank
|
|
469
|
+
LIMIT ?
|
|
408
470
|
`);
|
|
409
471
|
// Fuzzy path
|
|
410
472
|
this.#stmtFuzzyVocab = this.#db.prepare("SELECT word FROM vocabulary WHERE length(word) BETWEEN ? AND ?");
|
|
@@ -514,17 +576,34 @@ export class ContentStore {
|
|
|
514
576
|
};
|
|
515
577
|
}
|
|
516
578
|
// ── Search ──
|
|
517
|
-
|
|
579
|
+
#mapSearchRows(rows) {
|
|
580
|
+
return rows.map((r) => ({
|
|
581
|
+
title: r.title,
|
|
582
|
+
content: r.content,
|
|
583
|
+
source: r.label,
|
|
584
|
+
rank: r.rank,
|
|
585
|
+
contentType: r.content_type,
|
|
586
|
+
highlighted: r.highlighted,
|
|
587
|
+
}));
|
|
588
|
+
}
|
|
589
|
+
#sourceFilterParam(source, sourceMatchMode) {
|
|
590
|
+
return sourceMatchMode === "exact" ? source : `%${source}%`;
|
|
591
|
+
}
|
|
592
|
+
search(query, limit = 3, source, mode = "AND", contentType, sourceMatchMode = "like") {
|
|
518
593
|
const sanitized = sanitizeQuery(query, mode);
|
|
519
594
|
let stmt;
|
|
520
595
|
let params;
|
|
521
596
|
if (source && contentType) {
|
|
522
|
-
stmt =
|
|
523
|
-
|
|
597
|
+
stmt = sourceMatchMode === "exact"
|
|
598
|
+
? this.#stmtSearchPorterExactContentType
|
|
599
|
+
: this.#stmtSearchPorterFilteredContentType;
|
|
600
|
+
params = [sanitized, this.#sourceFilterParam(source, sourceMatchMode), contentType, limit];
|
|
524
601
|
}
|
|
525
602
|
else if (source) {
|
|
526
|
-
stmt =
|
|
527
|
-
|
|
603
|
+
stmt = sourceMatchMode === "exact"
|
|
604
|
+
? this.#stmtSearchPorterExact
|
|
605
|
+
: this.#stmtSearchPorterFiltered;
|
|
606
|
+
params = [sanitized, this.#sourceFilterParam(source, sourceMatchMode), limit];
|
|
528
607
|
}
|
|
529
608
|
else if (contentType) {
|
|
530
609
|
stmt = this.#stmtSearchPorterContentType;
|
|
@@ -534,30 +613,26 @@ export class ContentStore {
|
|
|
534
613
|
stmt = this.#stmtSearchPorter;
|
|
535
614
|
params = [sanitized, limit];
|
|
536
615
|
}
|
|
537
|
-
|
|
538
|
-
return rows.map((r) => ({
|
|
539
|
-
title: r.title,
|
|
540
|
-
content: r.content,
|
|
541
|
-
source: r.label,
|
|
542
|
-
rank: r.rank,
|
|
543
|
-
contentType: r.content_type,
|
|
544
|
-
highlighted: r.highlighted,
|
|
545
|
-
}));
|
|
616
|
+
return this.#mapSearchRows(stmt.all(...params));
|
|
546
617
|
}
|
|
547
618
|
// ── Trigram Search (Layer 2) ──
|
|
548
|
-
searchTrigram(query, limit = 3, source, mode = "AND", contentType) {
|
|
619
|
+
searchTrigram(query, limit = 3, source, mode = "AND", contentType, sourceMatchMode = "like") {
|
|
549
620
|
const sanitized = sanitizeTrigramQuery(query, mode);
|
|
550
621
|
if (!sanitized)
|
|
551
622
|
return [];
|
|
552
623
|
let stmt;
|
|
553
624
|
let params;
|
|
554
625
|
if (source && contentType) {
|
|
555
|
-
stmt =
|
|
556
|
-
|
|
626
|
+
stmt = sourceMatchMode === "exact"
|
|
627
|
+
? this.#stmtSearchTrigramExactContentType
|
|
628
|
+
: this.#stmtSearchTrigramFilteredContentType;
|
|
629
|
+
params = [sanitized, this.#sourceFilterParam(source, sourceMatchMode), contentType, limit];
|
|
557
630
|
}
|
|
558
631
|
else if (source) {
|
|
559
|
-
stmt =
|
|
560
|
-
|
|
632
|
+
stmt = sourceMatchMode === "exact"
|
|
633
|
+
? this.#stmtSearchTrigramExact
|
|
634
|
+
: this.#stmtSearchTrigramFiltered;
|
|
635
|
+
params = [sanitized, this.#sourceFilterParam(source, sourceMatchMode), limit];
|
|
561
636
|
}
|
|
562
637
|
else if (contentType) {
|
|
563
638
|
stmt = this.#stmtSearchTrigramContentType;
|
|
@@ -567,15 +642,7 @@ export class ContentStore {
|
|
|
567
642
|
stmt = this.#stmtSearchTrigram;
|
|
568
643
|
params = [sanitized, limit];
|
|
569
644
|
}
|
|
570
|
-
|
|
571
|
-
return rows.map((r) => ({
|
|
572
|
-
title: r.title,
|
|
573
|
-
content: r.content,
|
|
574
|
-
source: r.label,
|
|
575
|
-
rank: r.rank,
|
|
576
|
-
contentType: r.content_type,
|
|
577
|
-
highlighted: r.highlighted,
|
|
578
|
-
}));
|
|
645
|
+
return this.#mapSearchRows(stmt.all(...params));
|
|
579
646
|
}
|
|
580
647
|
// ── Fuzzy Correction (Layer 3) ──
|
|
581
648
|
fuzzyCorrect(query) {
|
|
@@ -598,11 +665,11 @@ export class ContentStore {
|
|
|
598
665
|
return bestDist <= maxDist ? bestWord : null;
|
|
599
666
|
}
|
|
600
667
|
// ── Reciprocal Rank Fusion (Cormack et al. 2009) ──
|
|
601
|
-
#rrfSearch(query, limit, source, contentType) {
|
|
668
|
+
#rrfSearch(query, limit, source, contentType, sourceMatchMode = "like") {
|
|
602
669
|
const K = 60; // Standard RRF constant
|
|
603
670
|
const fetchLimit = Math.max(limit * 2, 10);
|
|
604
|
-
const porterResults = this.search(query, fetchLimit, source, "OR", contentType);
|
|
605
|
-
const trigramResults = this.searchTrigram(query, fetchLimit, source, "OR", contentType);
|
|
671
|
+
const porterResults = this.search(query, fetchLimit, source, "OR", contentType, sourceMatchMode);
|
|
672
|
+
const trigramResults = this.searchTrigram(query, fetchLimit, source, "OR", contentType, sourceMatchMode);
|
|
606
673
|
const scoreMap = new Map();
|
|
607
674
|
const key = (r) => `${r.source}::${r.title}`;
|
|
608
675
|
for (const [i, r] of porterResults.entries()) {
|
|
@@ -655,9 +722,9 @@ export class ContentStore {
|
|
|
655
722
|
.map(({ result }) => result);
|
|
656
723
|
}
|
|
657
724
|
// ── Unified Fallback Search ──
|
|
658
|
-
searchWithFallback(query, limit = 3, source, contentType) {
|
|
725
|
+
searchWithFallback(query, limit = 3, source, contentType, sourceMatchMode = "like") {
|
|
659
726
|
// Step 1: RRF fusion (porter OR + trigram OR → merge)
|
|
660
|
-
const rrfResults = this.#rrfSearch(query, limit, source, contentType);
|
|
727
|
+
const rrfResults = this.#rrfSearch(query, limit, source, contentType, sourceMatchMode);
|
|
661
728
|
if (rrfResults.length > 0) {
|
|
662
729
|
const reranked = this.#applyProximityReranking(rrfResults, query);
|
|
663
730
|
return reranked.map((r) => ({ ...r, matchLayer: "rrf" }));
|
|
@@ -672,7 +739,7 @@ export class ContentStore {
|
|
|
672
739
|
const correctedWords = words.map((w) => this.fuzzyCorrect(w) ?? w);
|
|
673
740
|
const correctedQuery = correctedWords.join(" ");
|
|
674
741
|
if (correctedQuery !== original) {
|
|
675
|
-
const fuzzyResults = this.#rrfSearch(correctedQuery, limit, source, contentType);
|
|
742
|
+
const fuzzyResults = this.#rrfSearch(correctedQuery, limit, source, contentType, sourceMatchMode);
|
|
676
743
|
if (fuzzyResults.length > 0) {
|
|
677
744
|
const reranked = this.#applyProximityReranking(fuzzyResults, correctedQuery);
|
|
678
745
|
return reranked.map((r) => ({ ...r, matchLayer: "rrf-fuzzy" }));
|