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.
@@ -6,14 +6,14 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Claude Code plugins by Mert Koseoğlu",
9
- "version": "1.0.77"
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.77",
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.77",
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.77",
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.77",
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
- const content = r.content.toLowerCase();
767
- const positions = terms.map((t) => findAllPositions(content, t));
768
- // If any term is missing from content, no proximity boost
769
- if (positions.some((p) => p.length === 0)) {
770
- return { result: r, boost: 0 };
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
- const minSpan = findMinSpan(positions);
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;