context-mode 1.0.32 → 1.0.34

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.32"
9
+ "version": "1.0.34"
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.32",
16
+ "version": "1.0.34",
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.32",
3
+ "version": "1.0.34",
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.32",
6
+ "version": "1.0.34",
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.32",
3
+ "version": "1.0.34",
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/server.js CHANGED
@@ -115,6 +115,8 @@ const sessionStats = {
115
115
  bytesReturned: {},
116
116
  bytesIndexed: 0,
117
117
  bytesSandboxed: 0, // network I/O consumed inside sandbox (never enters context)
118
+ cacheHits: 0,
119
+ cacheBytesSaved: 0, // bytes avoided by TTL cache hits
118
120
  sessionStart: Date.now(),
119
121
  };
120
122
  function trackResponse(toolName, response) {
@@ -802,7 +804,8 @@ function coerceCommandsArray(val) {
802
804
  }
803
805
  server.registerTool("ctx_search", {
804
806
  title: "Search Indexed Content",
805
- description: "Search indexed content. Pass ALL search questions as queries array in ONE call.\n\n" +
807
+ description: "Search indexed content. Requires prior indexing via ctx_batch_execute, ctx_index, or ctx_fetch_and_index. " +
808
+ "Pass ALL search questions as queries array in ONE call.\n\n" +
806
809
  "TIPS: 2-4 specific terms per query. Use 'source' to scope results.",
807
810
  inputSchema: z.object({
808
811
  queries: z.preprocess(coerceJsonArray, z
@@ -826,6 +829,23 @@ server.registerTool("ctx_search", {
826
829
  }, async (params) => {
827
830
  try {
828
831
  const store = getStore();
832
+ // Guard: redirect when the index is empty — ctx_search is a follow-up
833
+ // tool that requires prior indexing. Guide the model to the right tool.
834
+ if (store.getStats().chunks === 0) {
835
+ return trackResponse("ctx_search", {
836
+ content: [{
837
+ type: "text",
838
+ text: "Knowledge base is empty — no content has been indexed yet.\n\n" +
839
+ "ctx_search is a follow-up tool that queries previously indexed content. " +
840
+ "To gather and index content first, use:\n" +
841
+ " • ctx_batch_execute(commands, queries) — run commands, auto-index output, and search in one call\n" +
842
+ " • ctx_fetch_and_index(url) — fetch a URL, index it, then search with ctx_search\n" +
843
+ " • ctx_index(content, source) — manually index text content\n\n" +
844
+ "After indexing, ctx_search becomes available for follow-up queries.",
845
+ }],
846
+ isError: true,
847
+ });
848
+ }
829
849
  const raw = params;
830
850
  // Normalize: accept both query (string) and queries (array)
831
851
  const queryList = [];
@@ -1025,6 +1045,10 @@ server.registerTool("ctx_fetch_and_index", {
1025
1045
  const ageHours = Math.floor(ageMs / (60 * 60 * 1000));
1026
1046
  const ageMin = Math.floor(ageMs / (60 * 1000));
1027
1047
  const ageStr = ageHours > 0 ? `${ageHours}h ago` : ageMin > 0 ? `${ageMin}m ago` : "just now";
1048
+ // Track cache savings — estimate ~1.6KB per chunk (average indexed content size)
1049
+ const estimatedBytes = meta.chunkCount * 1600;
1050
+ sessionStats.cacheHits++;
1051
+ sessionStats.cacheBytesSaved += estimatedBytes;
1028
1052
  return trackResponse("ctx_fetch_and_index", {
1029
1053
  content: [{
1030
1054
  type: "text",
@@ -1366,6 +1390,17 @@ server.registerTool("ctx_stats", {
1366
1390
  if (keptOut > 0) {
1367
1391
  lines.push("", `Without context-mode, **${kb(totalProcessed)}** of raw output would flood your context window. Instead, **${reductionPct}%** stayed in sandbox.`);
1368
1392
  }
1393
+ // Cache savings section
1394
+ if (sessionStats.cacheHits > 0 || sessionStats.cacheBytesSaved > 0) {
1395
+ const totalWithCache = totalProcessed + sessionStats.cacheBytesSaved;
1396
+ const totalSavingsRatio = totalWithCache / Math.max(totalBytesReturned, 1);
1397
+ const ttlHoursLeft = Math.max(0, 24 - Math.floor((Date.now() - sessionStats.sessionStart) / (60 * 60 * 1000)));
1398
+ lines.push("", `### TTL Cache`, "", `| Metric | Value |`, `|--------|------:|`, `| Cache hits | **${sessionStats.cacheHits}** |`, `| Data avoided by cache | **${kb(sessionStats.cacheBytesSaved)}** |`, `| Network requests saved | **${sessionStats.cacheHits}** |`, `| TTL remaining | **~${ttlHoursLeft}h** |`, "", `Content was already indexed in the knowledge base — ${sessionStats.cacheHits} fetch${sessionStats.cacheHits > 1 ? "es" : ""} skipped entirely. **${kb(sessionStats.cacheBytesSaved)}** of network I/O avoided. Search results served directly from local FTS5 index.`);
1399
+ // Update total savings to include cache
1400
+ if (totalSavingsRatio > savingsRatio) {
1401
+ lines.push("", `**Total context savings (sandbox + cache): ${totalSavingsRatio.toFixed(1)}x** — ${kb(totalWithCache)} processed, only ${kb(totalBytesReturned)} entered context.`);
1402
+ }
1403
+ }
1369
1404
  }
1370
1405
  // ── Session Continuity ──
1371
1406
  try {