codex-dev-mcp-suite 1.1.0 → 1.3.0
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/.env.example +3 -0
- package/CHANGELOG.md +33 -0
- package/README.md +111 -0
- package/bin/_meta.mjs +69 -0
- package/bin/checkpoint.mjs +8 -1
- package/bin/context-pack.mjs +8 -1
- package/bin/devjournal.mjs +8 -1
- package/bin/project-memory.mjs +8 -1
- package/bin/prune.mjs +158 -0
- package/bin/stats.mjs +101 -0
- package/devjournal/provider-chain.js +63 -10
- package/devjournal/rerank.js +29 -13
- package/docs/configuration.md +14 -0
- package/lib/prune.js +114 -0
- package/lib/stats.js +269 -0
- package/package.json +9 -4
- package/project-memory/provider-chain.js +63 -10
- package/project-memory/rerank.js +29 -13
- package/project-memory/server.js +29 -0
package/project-memory/rerank.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LLM reranker via any OpenAI-compatible /v1/chat/completions model.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Tries provider chain slots in order, skipping any currently cooling down
|
|
4
|
+
* after a recent failure. Degrades gracefully: returns null on any failure so
|
|
5
|
+
* callers fall back to keyword ordering. Never throws. Never logs keys/bodies.
|
|
6
6
|
*/
|
|
7
7
|
import http from "http";
|
|
8
8
|
import https from "https";
|
|
9
9
|
import { URL } from "url";
|
|
10
10
|
import { deterministicEnabled } from "./env.js";
|
|
11
|
-
import { providerChainConfig } from "./provider-chain.js";
|
|
11
|
+
import { providerChainConfig, isCoolingDown, recordOutcome } from "./provider-chain.js";
|
|
12
12
|
|
|
13
13
|
const TIMEOUT_MS = Number(process.env.RERANK_TIMEOUT_MS || 30000);
|
|
14
14
|
const ENABLED = process.env.RERANK_ENABLED !== "0";
|
|
@@ -25,10 +25,19 @@ export function rerankConfig() {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
function providerKey(provider) {
|
|
29
|
+
return `${provider.label}|${provider.base}|${provider.model}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolves { content, retryable }. retryable=true means try the next provider
|
|
34
|
+
* (timeout, network error, 429, or 5xx). retryable=false with null content
|
|
35
|
+
* means a definitive non-retryable response (e.g. 4xx auth).
|
|
36
|
+
*/
|
|
28
37
|
function chatWithProvider(provider, messages) {
|
|
29
38
|
return new Promise((resolve) => {
|
|
30
39
|
let u;
|
|
31
|
-
try { u = new URL("/v1/chat/completions", provider.base); } catch { return resolve(null); }
|
|
40
|
+
try { u = new URL("/v1/chat/completions", provider.base); } catch { return resolve({ content: null, retryable: false }); }
|
|
32
41
|
const payload = Buffer.from(JSON.stringify({ model: provider.model, stream: false, temperature: 0, max_tokens: 200, messages }));
|
|
33
42
|
const lib = u.protocol === "https:" ? https : http;
|
|
34
43
|
const req = lib.request(
|
|
@@ -44,10 +53,14 @@ function chatWithProvider(provider, messages) {
|
|
|
44
53
|
let body = "";
|
|
45
54
|
res.on("data", (c) => (body += c));
|
|
46
55
|
res.on("end", () => {
|
|
47
|
-
|
|
56
|
+
const status = res.statusCode || 0;
|
|
57
|
+
if (status !== 200) {
|
|
58
|
+
const retryable = status === 429 || status >= 500;
|
|
59
|
+
return resolve({ content: null, retryable });
|
|
60
|
+
}
|
|
48
61
|
try {
|
|
49
62
|
const j = JSON.parse(body);
|
|
50
|
-
return resolve(j?.choices?.[0]?.message?.content ?? null);
|
|
63
|
+
return resolve({ content: j?.choices?.[0]?.message?.content ?? null, retryable: false });
|
|
51
64
|
} catch { /* maybe SSE */ }
|
|
52
65
|
try {
|
|
53
66
|
let acc = "";
|
|
@@ -59,13 +72,13 @@ function chatWithProvider(provider, messages) {
|
|
|
59
72
|
const j = JSON.parse(d);
|
|
60
73
|
acc += j?.choices?.[0]?.delta?.content || j?.choices?.[0]?.message?.content || "";
|
|
61
74
|
}
|
|
62
|
-
return resolve(acc || null);
|
|
63
|
-
} catch { return resolve(null); }
|
|
75
|
+
return resolve({ content: acc || null, retryable: false });
|
|
76
|
+
} catch { return resolve({ content: null, retryable: true }); }
|
|
64
77
|
});
|
|
65
78
|
}
|
|
66
79
|
);
|
|
67
|
-
req.on("error", () => resolve(null));
|
|
68
|
-
req.on("timeout", () => { req.destroy(); resolve(null); });
|
|
80
|
+
req.on("error", () => resolve({ content: null, retryable: true }));
|
|
81
|
+
req.on("timeout", () => { req.destroy(); resolve({ content: null, retryable: true }); });
|
|
69
82
|
req.write(payload);
|
|
70
83
|
req.end();
|
|
71
84
|
});
|
|
@@ -75,8 +88,11 @@ async function chat(messages) {
|
|
|
75
88
|
if (!ENABLED || deterministicEnabled()) return null;
|
|
76
89
|
const { providers } = providerChainConfig();
|
|
77
90
|
for (const provider of providers) {
|
|
78
|
-
const
|
|
79
|
-
if (
|
|
91
|
+
const key = providerKey(provider);
|
|
92
|
+
if (isCoolingDown(key)) continue;
|
|
93
|
+
const { content, retryable } = await chatWithProvider(provider, messages);
|
|
94
|
+
if (content) { recordOutcome(key, true); return content; }
|
|
95
|
+
if (retryable) recordOutcome(key, false);
|
|
80
96
|
}
|
|
81
97
|
return null;
|
|
82
98
|
}
|
package/project-memory/server.js
CHANGED
|
@@ -29,6 +29,7 @@ import crypto from "crypto";
|
|
|
29
29
|
import { embed, embedOne, cosine, embeddingConfig } from "./embedding.js";
|
|
30
30
|
import { rerank, rerankConfig } from "./rerank.js";
|
|
31
31
|
import { deterministicEnabled } from "./env.js";
|
|
32
|
+
import { computeStats, formatText, formatJson } from "../lib/stats.js";
|
|
32
33
|
|
|
33
34
|
const VAULT_ROOT =
|
|
34
35
|
process.env.MEMORY_VAULT_DIR ||
|
|
@@ -220,6 +221,18 @@ class ProjectMemoryServer {
|
|
|
220
221
|
},
|
|
221
222
|
},
|
|
222
223
|
},
|
|
224
|
+
{
|
|
225
|
+
name: "memory_stats",
|
|
226
|
+
description: "Summarize local memory storage across vault / journal / checkpoints: totals, top projects, recent activity, and temp-slug cleanup candidates. Returns the same text as the `stats` CLI.",
|
|
227
|
+
inputSchema: {
|
|
228
|
+
type: "object",
|
|
229
|
+
properties: {
|
|
230
|
+
root: { type: "string", description: "Storage root (default: ~/.codex/memories or first env var of MEMORY_VAULT_DIR / JOURNAL_DIR / CHECKPOINT_DIR)" },
|
|
231
|
+
top: { type: "number", description: "How many entries in top-project / recent-activity lists (default 10)", default: 10 },
|
|
232
|
+
json: { type: "boolean", description: "Return machine-readable JSON instead of human text", default: false },
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
},
|
|
223
236
|
],
|
|
224
237
|
}));
|
|
225
238
|
|
|
@@ -233,6 +246,7 @@ class ProjectMemoryServer {
|
|
|
233
246
|
case "memory_get": return await this.get(args || {});
|
|
234
247
|
case "memory_delete": return await this.del(args || {});
|
|
235
248
|
case "memory_reindex": return await this.reindex(args || {});
|
|
249
|
+
case "memory_stats": return await this.stats(args || {});
|
|
236
250
|
default: throw new Error(`Unknown tool: ${name}`);
|
|
237
251
|
}
|
|
238
252
|
} catch (error) {
|
|
@@ -439,6 +453,21 @@ class ProjectMemoryServer {
|
|
|
439
453
|
return { content: [{ type: "text", text: `Reindex ${p.slug}: embedded ${done}, failed ${failed} (embeddings ${embeddingConfig().enabled ? "configured" : "not configured"}). ${failed ? "Failures usually mean the embedding model is unavailable right now." : ""}` }] };
|
|
440
454
|
}
|
|
441
455
|
|
|
456
|
+
async stats({ root: explicitRoot, top = 10, json = false } = {}) {
|
|
457
|
+
const root = explicitRoot
|
|
458
|
+
? path.resolve(explicitRoot)
|
|
459
|
+
: (process.env.MEMORY_VAULT_DIR
|
|
460
|
+
? path.dirname(process.env.MEMORY_VAULT_DIR)
|
|
461
|
+
: process.env.JOURNAL_DIR
|
|
462
|
+
? path.dirname(process.env.JOURNAL_DIR)
|
|
463
|
+
: process.env.CHECKPOINT_DIR
|
|
464
|
+
? path.dirname(process.env.CHECKPOINT_DIR)
|
|
465
|
+
: path.join(os.homedir(), ".codex", "memories"));
|
|
466
|
+
const stats = computeStats({ root, topLimit: top });
|
|
467
|
+
const text = json ? formatJson(stats) : formatText(stats);
|
|
468
|
+
return { content: [{ type: "text", text }] };
|
|
469
|
+
}
|
|
470
|
+
|
|
442
471
|
async run() {
|
|
443
472
|
const transport = new StdioServerTransport();
|
|
444
473
|
await this.server.connect(transport);
|