context-mode 1.0.28 → 1.0.29
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/server.js +28 -1
- package/build/store.d.ts +6 -0
- package/build/store.js +8 -0
- package/cli.bundle.mjs +105 -101
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +71 -67
|
@@ -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.29"
|
|
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.29",
|
|
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.29",
|
|
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.29",
|
|
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.29",
|
|
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
|
@@ -978,8 +978,35 @@ server.registerTool("ctx_fetch_and_index", {
|
|
|
978
978
|
.string()
|
|
979
979
|
.optional()
|
|
980
980
|
.describe("Label for the indexed content (e.g., 'React useEffect docs', 'Supabase Auth API')"),
|
|
981
|
+
force: z
|
|
982
|
+
.boolean()
|
|
983
|
+
.optional()
|
|
984
|
+
.describe("Skip cache and re-fetch even if content was recently indexed"),
|
|
981
985
|
}),
|
|
982
|
-
}, async ({ url, source }) => {
|
|
986
|
+
}, async ({ url, source, force }) => {
|
|
987
|
+
// TTL cache: if source was indexed within 24h, return cached hint
|
|
988
|
+
if (!force) {
|
|
989
|
+
const store = getStore();
|
|
990
|
+
const label = source ?? url;
|
|
991
|
+
const meta = store.getSourceMeta(label);
|
|
992
|
+
if (meta) {
|
|
993
|
+
const indexedAt = new Date(meta.indexedAt + "Z"); // SQLite datetime is UTC without Z
|
|
994
|
+
const ageMs = Date.now() - indexedAt.getTime();
|
|
995
|
+
const TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
996
|
+
if (ageMs < TTL_MS) {
|
|
997
|
+
const ageHours = Math.floor(ageMs / (60 * 60 * 1000));
|
|
998
|
+
const ageMin = Math.floor(ageMs / (60 * 1000));
|
|
999
|
+
const ageStr = ageHours > 0 ? `${ageHours}h ago` : ageMin > 0 ? `${ageMin}m ago` : "just now";
|
|
1000
|
+
return trackResponse("ctx_fetch_and_index", {
|
|
1001
|
+
content: [{
|
|
1002
|
+
type: "text",
|
|
1003
|
+
text: `Cached: **${meta.label}** — ${meta.chunkCount} sections, indexed ${ageStr} (fresh, TTL: 24h).\nTo refresh: call ctx_fetch_and_index again with \`force: true\`.\n\nYou MUST call search() to answer questions about this content — this cached response contains no content.\nUse: search(queries: [...], source: "${meta.label}")`,
|
|
1004
|
+
}],
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
// Stale (>24h) — fall through to re-fetch silently
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
983
1010
|
// Generate a unique temp file path for the subprocess to write fetched content.
|
|
984
1011
|
// This bypasses the executor's 100KB stdout truncation — content goes file→handler directly.
|
|
985
1012
|
const outputPath = join(tmpdir(), `ctx-fetch-${Date.now()}-${Math.random().toString(36).slice(2)}.dat`);
|
package/build/store.d.ts
CHANGED
|
@@ -41,6 +41,12 @@ export declare class ContentStore {
|
|
|
41
41
|
searchTrigram(query: string, limit?: number, source?: string, mode?: "AND" | "OR", contentType?: "code" | "prose"): SearchResult[];
|
|
42
42
|
fuzzyCorrect(query: string): string | null;
|
|
43
43
|
searchWithFallback(query: string, limit?: number, source?: string, contentType?: "code" | "prose"): SearchResult[];
|
|
44
|
+
getSourceMeta(label: string): {
|
|
45
|
+
label: string;
|
|
46
|
+
chunkCount: number;
|
|
47
|
+
codeChunkCount: number;
|
|
48
|
+
indexedAt: string;
|
|
49
|
+
} | null;
|
|
44
50
|
listSources(): Array<{
|
|
45
51
|
label: string;
|
|
46
52
|
chunkCount: number;
|
package/build/store.js
CHANGED
|
@@ -194,6 +194,7 @@ export class ContentStore {
|
|
|
194
194
|
#stmtSourceChunkCount;
|
|
195
195
|
#stmtChunkContent;
|
|
196
196
|
#stmtStats;
|
|
197
|
+
#stmtSourceMeta;
|
|
197
198
|
constructor(dbPath) {
|
|
198
199
|
const Database = loadDatabase();
|
|
199
200
|
this.#dbPath =
|
|
@@ -385,6 +386,7 @@ export class ContentStore {
|
|
|
385
386
|
ORDER BY c.rowid`);
|
|
386
387
|
this.#stmtSourceChunkCount = this.#db.prepare("SELECT chunk_count FROM sources WHERE id = ?");
|
|
387
388
|
this.#stmtChunkContent = this.#db.prepare("SELECT content FROM chunks WHERE source_id = ?");
|
|
389
|
+
this.#stmtSourceMeta = this.#db.prepare("SELECT label, chunk_count, code_chunk_count, indexed_at FROM sources WHERE label = ?");
|
|
388
390
|
this.#stmtStats = this.#db.prepare(`
|
|
389
391
|
SELECT
|
|
390
392
|
(SELECT COUNT(*) FROM sources) AS sources,
|
|
@@ -648,6 +650,12 @@ export class ContentStore {
|
|
|
648
650
|
return [];
|
|
649
651
|
}
|
|
650
652
|
// ── Sources ──
|
|
653
|
+
getSourceMeta(label) {
|
|
654
|
+
const row = this.#stmtSourceMeta.get(label);
|
|
655
|
+
if (!row)
|
|
656
|
+
return null;
|
|
657
|
+
return { label: row.label, chunkCount: row.chunk_count, codeChunkCount: row.code_chunk_count, indexedAt: row.indexed_at };
|
|
658
|
+
}
|
|
651
659
|
listSources() {
|
|
652
660
|
return this.#stmtListSources.all();
|
|
653
661
|
}
|