oioxo-mcp 0.1.0 → 0.1.2
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/dist/cli/login.js +4 -2
- package/dist/core/capsule.js +46 -5
- package/package.json +1 -1
package/dist/cli/login.js
CHANGED
|
@@ -29,9 +29,11 @@ const SUCCESS_HTML = `<!doctype html><html><head><meta charset="utf-8"><title>OI
|
|
|
29
29
|
</div></body></html>`;
|
|
30
30
|
function openBrowser(url) {
|
|
31
31
|
const platform = process.platform;
|
|
32
|
-
// `start`
|
|
32
|
+
// Windows: NOT `cmd /c start` — cmd treats `&` as a command separator and
|
|
33
|
+
// truncates the URL at the first query param (a real user hit this: the
|
|
34
|
+
// page opened without the state nonce). explorer.exe passes it verbatim.
|
|
33
35
|
if (platform === 'win32')
|
|
34
|
-
spawn('
|
|
36
|
+
spawn('explorer.exe', [url], { detached: true, stdio: 'ignore' }).unref();
|
|
35
37
|
else if (platform === 'darwin')
|
|
36
38
|
spawn('open', [url], { detached: true, stdio: 'ignore' }).unref();
|
|
37
39
|
else
|
package/dist/core/capsule.js
CHANGED
|
@@ -6,6 +6,14 @@ export function buildCapsule(query, files, index, opts = {}) {
|
|
|
6
6
|
const maxFiles = opts.maxFiles ?? 10;
|
|
7
7
|
const maxChars = opts.maxChars ?? 24_000;
|
|
8
8
|
const byPath = new Map(files.map((f) => [f.path, f]));
|
|
9
|
+
// TINY-PROJECT fast path: when everything fits in one capsule, retrieval
|
|
10
|
+
// theater only adds headers. Ship the whole project verbatim and claim ZERO
|
|
11
|
+
// savings — honesty here is what makes the 90% on real repos believable.
|
|
12
|
+
const totalChars = files.reduce((n, f) => n + f.content.length, 0);
|
|
13
|
+
if (totalChars > 0 && totalChars <= maxChars * 0.8) {
|
|
14
|
+
const text = files.map((f) => `// ${f.path}\n${f.content}`).join('\n\n');
|
|
15
|
+
return { text, files: files.map((f) => f.path), savedTokens: 0, chunks: [] };
|
|
16
|
+
}
|
|
9
17
|
const chunks = index.retrieve(query, topChunks);
|
|
10
18
|
// Anchors: distinct files behind the top chunks (order of first appearance).
|
|
11
19
|
const anchors = [];
|
|
@@ -26,17 +34,50 @@ export function buildCapsule(query, files, index, opts = {}) {
|
|
|
26
34
|
}
|
|
27
35
|
const parts = [];
|
|
28
36
|
const anchorSet = new Set(anchors);
|
|
29
|
-
// 1) Anchor
|
|
37
|
+
// 1) Anchor excerpts. Chunks overlap by design (12-line windows), so emit
|
|
38
|
+
// MERGED line ranges per file — overlap shipped twice is pure waste. And
|
|
39
|
+
// when the merged range covers most of a small file, ship the whole file
|
|
40
|
+
// once instead (simpler for the agent, fewer tokens than range headers).
|
|
41
|
+
const rangesByFile = new Map();
|
|
30
42
|
for (const c of chunks) {
|
|
31
|
-
|
|
43
|
+
const list = rangesByFile.get(c.path) ?? [];
|
|
44
|
+
list.push({ start: c.startLine, end: c.endLine });
|
|
45
|
+
rangesByFile.set(c.path, list);
|
|
32
46
|
}
|
|
33
|
-
|
|
47
|
+
for (const [p, ranges] of rangesByFile) {
|
|
48
|
+
const f = byPath.get(p);
|
|
49
|
+
if (!f)
|
|
50
|
+
continue;
|
|
51
|
+
const lines = f.content.split('\n');
|
|
52
|
+
ranges.sort((a, b) => a.start - b.start);
|
|
53
|
+
const merged = [];
|
|
54
|
+
for (const r of ranges) {
|
|
55
|
+
const last = merged[merged.length - 1];
|
|
56
|
+
if (last && r.start <= last.end + 1)
|
|
57
|
+
last.end = Math.max(last.end, r.end);
|
|
58
|
+
else
|
|
59
|
+
merged.push({ ...r });
|
|
60
|
+
}
|
|
61
|
+
const covered = merged.reduce((n, r) => n + (r.end - r.start + 1), 0);
|
|
62
|
+
if (covered >= lines.length * 0.7) {
|
|
63
|
+
parts.push(`// ${p} (full)\n${f.content}`);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
for (const r of merged)
|
|
67
|
+
parts.push(`// ${p}:${r.start}-${r.end}\n${lines.slice(r.start - 1, r.end).join('\n')}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// 2) Neighbor files: skeletons only — unless the file is so small that its
|
|
71
|
+
// skeleton wouldn't be cheaper, in which case skip it (the agent can ask).
|
|
34
72
|
for (const p of slice) {
|
|
35
73
|
if (anchorSet.has(p))
|
|
36
74
|
continue;
|
|
37
75
|
const f = byPath.get(p);
|
|
38
|
-
if (f)
|
|
39
|
-
|
|
76
|
+
if (!f)
|
|
77
|
+
continue;
|
|
78
|
+
const skel = fileSkeleton(f.path, f.content);
|
|
79
|
+
if (skel.length < f.content.length * 0.8)
|
|
80
|
+
parts.push(skel);
|
|
40
81
|
}
|
|
41
82
|
let text = parts.join('\n\n');
|
|
42
83
|
if (text.length > maxChars)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oioxo-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "OIOXO context engine for AI coding agents — feeds Claude Code, Copilot, Cursor and any MCP-capable agent the minimal relevant slice of your codebase, on-device, so your tokens go further.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"type": "module",
|