context-mode 1.0.77 → 1.0.79
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/build/db-base.js +9 -0
- package/build/store.d.ts +1 -0
- package/build/store.js +45 -14
- package/cli.bundle.mjs +68 -68
- package/hooks/session-db.bundle.mjs +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +46 -46
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Claude Code plugins by Mert Koseoğlu",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.79"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "context-mode",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
16
|
-
"version": "1.0.
|
|
16
|
+
"version": "1.0.79",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Mert Koseoğlu"
|
|
19
19
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.79",
|
|
4
4
|
"description": "MCP server that saves 98% of your context window with session continuity. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and automatic state restore across compactions.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Context Mode",
|
|
4
4
|
"kind": "tool",
|
|
5
5
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.79",
|
|
7
7
|
"sandbox": {
|
|
8
8
|
"mode": "permissive",
|
|
9
9
|
"filesystem_access": "full",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.79",
|
|
4
4
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
package/build/db-base.js
CHANGED
|
@@ -222,6 +222,15 @@ export function loadDatabase() {
|
|
|
222
222
|
export function applyWALPragmas(db) {
|
|
223
223
|
db.pragma("journal_mode = WAL");
|
|
224
224
|
db.pragma("synchronous = NORMAL");
|
|
225
|
+
// Memory-map the DB file for read-heavy FTS5 search workloads.
|
|
226
|
+
// Eliminates read() syscalls — the kernel serves pages directly from
|
|
227
|
+
// the page cache. 256MB is a safe upper bound (SQLite only maps up to
|
|
228
|
+
// the actual file size). Falls back gracefully on platforms where mmap
|
|
229
|
+
// is unavailable or restricted.
|
|
230
|
+
try {
|
|
231
|
+
db.pragma("mmap_size = 268435456");
|
|
232
|
+
}
|
|
233
|
+
catch { /* unsupported runtime */ }
|
|
225
234
|
}
|
|
226
235
|
// ─────────────────────────────────────────────────────────
|
|
227
236
|
// DB file helpers
|
package/build/store.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export declare function cleanupStaleDBs(): number;
|
|
|
23
23
|
export declare function cleanupStaleContentDBs(contentDir: string, maxAgeDays: number): number;
|
|
24
24
|
export declare class ContentStore {
|
|
25
25
|
#private;
|
|
26
|
+
static readonly OPTIMIZE_EVERY = 50;
|
|
26
27
|
constructor(dbPath?: string);
|
|
27
28
|
/** Delete this session's DB files. Call on process exit. */
|
|
28
29
|
cleanup(): void;
|
package/build/store.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Use for documentation, API references, and any content where
|
|
8
8
|
* you need EXACT text later — not summaries.
|
|
9
9
|
*/
|
|
10
|
+
var _a;
|
|
10
11
|
import { loadDatabase, applyWALPragmas, closeDB, cleanOrphanedWALFiles, withRetry, deleteDBFiles, isSQLiteCorruptionError } from "./db-base.js";
|
|
11
12
|
import { readFileSync, readdirSync, unlinkSync, existsSync, statSync } from "node:fs";
|
|
12
13
|
import { tmpdir } from "node:os";
|
|
@@ -263,6 +264,11 @@ export class ContentStore {
|
|
|
263
264
|
#stmtChunkContent;
|
|
264
265
|
#stmtStats;
|
|
265
266
|
#stmtSourceMeta;
|
|
267
|
+
// FTS5 optimization: track inserts and optimize periodically to defragment
|
|
268
|
+
// the index. FTS5 b-trees fragment over many insert/delete cycles, degrading
|
|
269
|
+
// search performance. SQLite's built-in 'optimize' merges b-tree segments.
|
|
270
|
+
#insertCount = 0;
|
|
271
|
+
static OPTIMIZE_EVERY = 50;
|
|
266
272
|
constructor(dbPath) {
|
|
267
273
|
const Database = loadDatabase();
|
|
268
274
|
this.#dbPath =
|
|
@@ -564,7 +570,7 @@ export class ContentStore {
|
|
|
564
570
|
return this.#insertChunks([], source, "");
|
|
565
571
|
}
|
|
566
572
|
const chunks = this.#chunkPlainText(content, linesPerChunk);
|
|
567
|
-
return this.#insertChunks(chunks.map((c) => ({ ...c, hasCode: false })), source, content);
|
|
573
|
+
return withRetry(() => this.#insertChunks(chunks.map((c) => ({ ...c, hasCode: false })), source, content));
|
|
568
574
|
}
|
|
569
575
|
// ── Index JSON ──
|
|
570
576
|
/**
|
|
@@ -590,7 +596,7 @@ export class ContentStore {
|
|
|
590
596
|
if (chunks.length === 0) {
|
|
591
597
|
return this.indexPlainText(content, source);
|
|
592
598
|
}
|
|
593
|
-
return this.#insertChunks(chunks, source, content);
|
|
599
|
+
return withRetry(() => this.#insertChunks(chunks, source, content));
|
|
594
600
|
}
|
|
595
601
|
// ── Shared DB Insertion ──
|
|
596
602
|
/**
|
|
@@ -623,6 +629,14 @@ export class ContentStore {
|
|
|
623
629
|
const sourceId = transaction();
|
|
624
630
|
if (text)
|
|
625
631
|
this.#extractAndStoreVocabulary(text);
|
|
632
|
+
// Periodically optimize FTS5 indexes to merge b-tree segments.
|
|
633
|
+
// Fragmentation accumulates over insert/delete cycles (dedup re-indexes
|
|
634
|
+
// every source on update). The 'optimize' command merges segments into
|
|
635
|
+
// a single b-tree, improving search latency for long-running sessions.
|
|
636
|
+
this.#insertCount++;
|
|
637
|
+
if (this.#insertCount % _a.OPTIMIZE_EVERY === 0) {
|
|
638
|
+
this.#optimizeFTS();
|
|
639
|
+
}
|
|
626
640
|
return {
|
|
627
641
|
sourceId,
|
|
628
642
|
label,
|
|
@@ -697,7 +711,7 @@ export class ContentStore {
|
|
|
697
711
|
stmt = this.#stmtSearchTrigram;
|
|
698
712
|
params = [sanitized, limit];
|
|
699
713
|
}
|
|
700
|
-
return this.#mapSearchRows(stmt.all(...params));
|
|
714
|
+
return withRetry(() => this.#mapSearchRows(stmt.all(...params)));
|
|
701
715
|
}
|
|
702
716
|
// ── Fuzzy Correction (Layer 3) ──
|
|
703
717
|
fuzzyCorrect(query) {
|
|
@@ -758,20 +772,27 @@ export class ContentStore {
|
|
|
758
772
|
.toLowerCase()
|
|
759
773
|
.split(/\s+/)
|
|
760
774
|
.filter((w) => w.length >= 2);
|
|
761
|
-
// Single-term queries: no reranking needed
|
|
762
|
-
if (terms.length < 2)
|
|
763
|
-
return results;
|
|
764
775
|
return results
|
|
765
776
|
.map((r) => {
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
//
|
|
769
|
-
|
|
770
|
-
|
|
777
|
+
// Title-match boost: query terms found in the chunk title get a boost.
|
|
778
|
+
// Code chunks get a stronger title boost (function/class names are high
|
|
779
|
+
// signal) while prose chunks get a moderate one (headings are useful but
|
|
780
|
+
// body carries more weight).
|
|
781
|
+
const titleLower = r.title.toLowerCase();
|
|
782
|
+
const titleHits = terms.filter((t) => titleLower.includes(t)).length;
|
|
783
|
+
const titleWeight = r.contentType === "code" ? 0.6 : 0.3;
|
|
784
|
+
const titleBoost = titleHits > 0 ? titleWeight * (titleHits / terms.length) : 0;
|
|
785
|
+
// Proximity boost for multi-term queries
|
|
786
|
+
let proximityBoost = 0;
|
|
787
|
+
if (terms.length >= 2) {
|
|
788
|
+
const content = r.content.toLowerCase();
|
|
789
|
+
const positions = terms.map((t) => findAllPositions(content, t));
|
|
790
|
+
if (!positions.some((p) => p.length === 0)) {
|
|
791
|
+
const minSpan = findMinSpan(positions);
|
|
792
|
+
proximityBoost = 1 / (1 + minSpan / Math.max(content.length, 1));
|
|
793
|
+
}
|
|
771
794
|
}
|
|
772
|
-
|
|
773
|
-
const boost = 1 / (1 + minSpan / Math.max(content.length, 1));
|
|
774
|
-
return { result: r, boost };
|
|
795
|
+
return { result: r, boost: titleBoost + proximityBoost };
|
|
775
796
|
})
|
|
776
797
|
.sort((a, b) => b.boost - a.boost || a.result.rank - b.result.rank)
|
|
777
798
|
.map(({ result }) => result);
|
|
@@ -897,7 +918,16 @@ export class ContentStore {
|
|
|
897
918
|
return 0;
|
|
898
919
|
}
|
|
899
920
|
}
|
|
921
|
+
/** Merge FTS5 b-tree segments for both porter and trigram indexes. */
|
|
922
|
+
#optimizeFTS() {
|
|
923
|
+
try {
|
|
924
|
+
this.#db.exec("INSERT INTO chunks(chunks) VALUES('optimize')");
|
|
925
|
+
this.#db.exec("INSERT INTO chunks_trigram(chunks_trigram) VALUES('optimize')");
|
|
926
|
+
}
|
|
927
|
+
catch { /* best effort — don't block indexing */ }
|
|
928
|
+
}
|
|
900
929
|
close() {
|
|
930
|
+
this.#optimizeFTS(); // defragment before close
|
|
901
931
|
closeDB(this.#db); // WAL checkpoint before close — important for persistent DBs
|
|
902
932
|
}
|
|
903
933
|
// ── Vocabulary Extraction ──
|
|
@@ -1163,3 +1193,4 @@ export class ContentStore {
|
|
|
1163
1193
|
return headingStack.map((h) => h.text).join(" > ");
|
|
1164
1194
|
}
|
|
1165
1195
|
}
|
|
1196
|
+
_a = ContentStore;
|