context-mode 0.9.15 → 0.9.16
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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/build/server.d.ts +7 -1
- package/build/server.js +66 -15
- package/build/store.d.ts +1 -0
- package/build/store.js +6 -2
- package/package.json +1 -1
- package/server.bundle.mjs +19 -17
|
@@ -13,7 +13,7 @@
|
|
|
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": "0.9.
|
|
16
|
+
"version": "0.9.16",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Mert Koseoğlu"
|
|
19
19
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.16",
|
|
4
4
|
"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.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
package/build/server.d.ts
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Parse FTS5 highlight markers to find match positions in the
|
|
4
|
+
* original (marker-free) text. Returns character offsets into the
|
|
5
|
+
* stripped content where each matched token begins.
|
|
6
|
+
*/
|
|
7
|
+
export declare function positionsFromHighlight(highlighted: string): number[];
|
|
8
|
+
export declare function extractSnippet(content: string, query: string, maxLen?: number, highlighted?: string): string;
|
package/build/server.js
CHANGED
|
@@ -51,25 +51,76 @@ const bunNote = hasBunRuntime()
|
|
|
51
51
|
// ─────────────────────────────────────────────────────────
|
|
52
52
|
// Helper: smart snippet extraction — returns windows around
|
|
53
53
|
// matching query terms instead of dumb truncation
|
|
54
|
+
//
|
|
55
|
+
// When `highlighted` is provided (from FTS5 `highlight()` with
|
|
56
|
+
// STX/ETX markers), match positions are derived from the markers.
|
|
57
|
+
// This is the authoritative source — FTS5 uses the exact same
|
|
58
|
+
// tokenizer that produced the BM25 match, so stemmed variants
|
|
59
|
+
// like "configuration" matching query "configure" are found
|
|
60
|
+
// correctly. Falls back to indexOf on raw terms when highlighted
|
|
61
|
+
// is absent (non-FTS codepath).
|
|
54
62
|
// ─────────────────────────────────────────────────────────
|
|
55
|
-
|
|
63
|
+
const STX = "\x02";
|
|
64
|
+
const ETX = "\x03";
|
|
65
|
+
/**
|
|
66
|
+
* Parse FTS5 highlight markers to find match positions in the
|
|
67
|
+
* original (marker-free) text. Returns character offsets into the
|
|
68
|
+
* stripped content where each matched token begins.
|
|
69
|
+
*/
|
|
70
|
+
export function positionsFromHighlight(highlighted) {
|
|
71
|
+
const positions = [];
|
|
72
|
+
let cleanOffset = 0;
|
|
73
|
+
let i = 0;
|
|
74
|
+
while (i < highlighted.length) {
|
|
75
|
+
if (highlighted[i] === STX) {
|
|
76
|
+
// Record position of this match in the clean text
|
|
77
|
+
positions.push(cleanOffset);
|
|
78
|
+
i++; // skip STX
|
|
79
|
+
// Advance through matched text until ETX
|
|
80
|
+
while (i < highlighted.length && highlighted[i] !== ETX) {
|
|
81
|
+
cleanOffset++;
|
|
82
|
+
i++;
|
|
83
|
+
}
|
|
84
|
+
if (i < highlighted.length)
|
|
85
|
+
i++; // skip ETX
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
cleanOffset++;
|
|
89
|
+
i++;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return positions;
|
|
93
|
+
}
|
|
94
|
+
/** Strip STX/ETX markers to recover original content. */
|
|
95
|
+
function stripMarkers(highlighted) {
|
|
96
|
+
return highlighted.replaceAll(STX, "").replaceAll(ETX, "");
|
|
97
|
+
}
|
|
98
|
+
export function extractSnippet(content, query, maxLen = 1500, highlighted) {
|
|
56
99
|
if (content.length <= maxLen)
|
|
57
100
|
return content;
|
|
58
|
-
|
|
59
|
-
.toLowerCase()
|
|
60
|
-
.split(/\s+/)
|
|
61
|
-
.filter((t) => t.length > 2);
|
|
62
|
-
const lower = content.toLowerCase();
|
|
63
|
-
// Find all positions where query terms appear
|
|
101
|
+
// Derive match positions from FTS5 highlight markers when available
|
|
64
102
|
const positions = [];
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
103
|
+
if (highlighted) {
|
|
104
|
+
for (const pos of positionsFromHighlight(highlighted)) {
|
|
105
|
+
positions.push(pos);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Fallback: indexOf on raw query terms (non-FTS codepath)
|
|
109
|
+
if (positions.length === 0) {
|
|
110
|
+
const terms = query
|
|
111
|
+
.toLowerCase()
|
|
112
|
+
.split(/\s+/)
|
|
113
|
+
.filter((t) => t.length > 2);
|
|
114
|
+
const lower = content.toLowerCase();
|
|
115
|
+
for (const term of terms) {
|
|
116
|
+
let idx = lower.indexOf(term);
|
|
117
|
+
while (idx !== -1) {
|
|
118
|
+
positions.push(idx);
|
|
119
|
+
idx = lower.indexOf(term, idx + 1);
|
|
120
|
+
}
|
|
70
121
|
}
|
|
71
122
|
}
|
|
72
|
-
// No
|
|
123
|
+
// No matches at all — return prefix
|
|
73
124
|
if (positions.length === 0) {
|
|
74
125
|
return content.slice(0, maxLen) + "\n…";
|
|
75
126
|
}
|
|
@@ -541,7 +592,7 @@ server.registerTool("search", {
|
|
|
541
592
|
.map((r, i) => {
|
|
542
593
|
const header = `--- [${r.source}] ---`;
|
|
543
594
|
const heading = `### ${r.title}`;
|
|
544
|
-
const snippet = extractSnippet(r.content, q);
|
|
595
|
+
const snippet = extractSnippet(r.content, q, 1500, r.highlighted);
|
|
545
596
|
return `${header}\n${heading}\n\n${snippet}`;
|
|
546
597
|
})
|
|
547
598
|
.join("\n\n");
|
|
@@ -822,7 +873,7 @@ server.registerTool("batch_execute", {
|
|
|
822
873
|
queryResults.push("");
|
|
823
874
|
if (results.length > 0) {
|
|
824
875
|
for (const r of results) {
|
|
825
|
-
const snippet = extractSnippet(r.content, query);
|
|
876
|
+
const snippet = extractSnippet(r.content, query, 1500, r.highlighted);
|
|
826
877
|
queryResults.push(`### ${r.title}`);
|
|
827
878
|
queryResults.push(snippet);
|
|
828
879
|
queryResults.push("");
|
package/build/store.d.ts
CHANGED
package/build/store.js
CHANGED
|
@@ -271,7 +271,8 @@ export class ContentStore {
|
|
|
271
271
|
chunks.content,
|
|
272
272
|
chunks.content_type,
|
|
273
273
|
sources.label,
|
|
274
|
-
bm25(chunks, 2.0, 1.0) AS rank
|
|
274
|
+
bm25(chunks, 2.0, 1.0) AS rank,
|
|
275
|
+
highlight(chunks, 1, char(2), char(3)) AS highlighted
|
|
275
276
|
FROM chunks
|
|
276
277
|
JOIN sources ON sources.id = chunks.source_id
|
|
277
278
|
WHERE chunks MATCH ? ${sourceFilter}
|
|
@@ -288,6 +289,7 @@ export class ContentStore {
|
|
|
288
289
|
source: r.label,
|
|
289
290
|
rank: r.rank,
|
|
290
291
|
contentType: r.content_type,
|
|
292
|
+
highlighted: r.highlighted,
|
|
291
293
|
}));
|
|
292
294
|
}
|
|
293
295
|
// ── Trigram Search (Layer 2) ──
|
|
@@ -302,7 +304,8 @@ export class ContentStore {
|
|
|
302
304
|
chunks_trigram.content,
|
|
303
305
|
chunks_trigram.content_type,
|
|
304
306
|
sources.label,
|
|
305
|
-
bm25(chunks_trigram, 2.0, 1.0) AS rank
|
|
307
|
+
bm25(chunks_trigram, 2.0, 1.0) AS rank,
|
|
308
|
+
highlight(chunks_trigram, 1, char(2), char(3)) AS highlighted
|
|
306
309
|
FROM chunks_trigram
|
|
307
310
|
JOIN sources ON sources.id = chunks_trigram.source_id
|
|
308
311
|
WHERE chunks_trigram MATCH ? ${sourceFilter}
|
|
@@ -319,6 +322,7 @@ export class ContentStore {
|
|
|
319
322
|
source: r.label,
|
|
320
323
|
rank: r.rank,
|
|
321
324
|
contentType: r.content_type,
|
|
325
|
+
highlighted: r.highlighted,
|
|
322
326
|
}));
|
|
323
327
|
}
|
|
324
328
|
// ── Fuzzy Correction (Layer 3) ──
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution, FTS5 knowledge base, and intent-driven search.",
|
|
6
6
|
"author": "Mert Koseoğlu",
|