@minhpnq1807/contextos 0.5.1 → 0.5.3
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/CHANGELOG.md +10 -0
- package/package.json +1 -1
- package/plugins/ctx/lib/embedding-scorer.js +68 -14
- package/plugins/ctx/lib/ruler-sync.js +18 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.3
|
|
4
|
+
|
|
5
|
+
- Skips project MCP imports whose command is an absolute path that does not exist, preventing placeholder paths such as `/home/user/.cargo/bin/mcp-rtk` from reaching Antigravity.
|
|
6
|
+
- Sanitizes Antigravity MCP config files during `ctx sync --rules` by removing existing MCP entries with missing absolute command paths.
|
|
7
|
+
|
|
8
|
+
## 0.5.2
|
|
9
|
+
|
|
10
|
+
- Recovers automatically from malformed `embeddings.db` files by moving the corrupt cache aside and recreating it.
|
|
11
|
+
- Writes the sql.js embedding cache with atomic temp-file rename to avoid partial DB files during concurrent hook/MCP writes.
|
|
12
|
+
|
|
3
13
|
## 0.5.1
|
|
4
14
|
|
|
5
15
|
- Fixes `ctx sync --skills` first-run ordering by running `skillshare init` before `skillshare backup`, matching skillshare's config requirement.
|
package/package.json
CHANGED
|
@@ -158,21 +158,11 @@ async function getCachedEmbedding({ cache, embedder, text, sources }) {
|
|
|
158
158
|
return embedding;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
async function openEmbeddingCache(dataDir) {
|
|
161
|
+
export async function openEmbeddingCache(dataDir) {
|
|
162
162
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
163
163
|
const cachePath = path.join(dataDir, "embeddings.db");
|
|
164
164
|
const SQL = await getSql();
|
|
165
|
-
const
|
|
166
|
-
const db = buffer?.length ? new SQL.Database(buffer) : new SQL.Database();
|
|
167
|
-
|
|
168
|
-
db.run(`
|
|
169
|
-
CREATE TABLE IF NOT EXISTS embeddings (
|
|
170
|
-
key TEXT PRIMARY KEY,
|
|
171
|
-
model TEXT NOT NULL,
|
|
172
|
-
vector TEXT NOT NULL,
|
|
173
|
-
updated_at TEXT NOT NULL
|
|
174
|
-
)
|
|
175
|
-
`);
|
|
165
|
+
const db = initializeEmbeddingDatabase(SQL, cachePath);
|
|
176
166
|
|
|
177
167
|
return {
|
|
178
168
|
path: cachePath,
|
|
@@ -191,15 +181,79 @@ async function openEmbeddingCache(dataDir) {
|
|
|
191
181
|
"INSERT OR REPLACE INTO embeddings (key, model, vector, updated_at) VALUES (?, ?, ?, ?)",
|
|
192
182
|
[key, DEFAULT_MODEL, JSON.stringify(vector), new Date().toISOString()]
|
|
193
183
|
);
|
|
194
|
-
|
|
184
|
+
writeDatabaseAtomically(cachePath, db);
|
|
195
185
|
},
|
|
196
186
|
close() {
|
|
197
|
-
|
|
187
|
+
writeDatabaseAtomically(cachePath, db);
|
|
198
188
|
db.close();
|
|
199
189
|
}
|
|
200
190
|
};
|
|
201
191
|
}
|
|
202
192
|
|
|
193
|
+
function initializeEmbeddingDatabase(SQL, cachePath) {
|
|
194
|
+
let db = openSqlDatabase(SQL, cachePath);
|
|
195
|
+
try {
|
|
196
|
+
ensureEmbeddingSchema(db);
|
|
197
|
+
return db;
|
|
198
|
+
} catch (error) {
|
|
199
|
+
try {
|
|
200
|
+
db.close();
|
|
201
|
+
} catch {
|
|
202
|
+
// Ignore close failures while recovering a corrupt cache.
|
|
203
|
+
}
|
|
204
|
+
quarantineMalformedCache(cachePath, error);
|
|
205
|
+
db = new SQL.Database();
|
|
206
|
+
ensureEmbeddingSchema(db);
|
|
207
|
+
writeDatabaseAtomically(cachePath, db);
|
|
208
|
+
return db;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function ensureEmbeddingSchema(db) {
|
|
213
|
+
db.run(`
|
|
214
|
+
CREATE TABLE IF NOT EXISTS embeddings (
|
|
215
|
+
key TEXT PRIMARY KEY,
|
|
216
|
+
model TEXT NOT NULL,
|
|
217
|
+
vector TEXT NOT NULL,
|
|
218
|
+
updated_at TEXT NOT NULL
|
|
219
|
+
)
|
|
220
|
+
`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function openSqlDatabase(SQL, cachePath) {
|
|
224
|
+
if (!fs.existsSync(cachePath)) return new SQL.Database();
|
|
225
|
+
const buffer = fs.readFileSync(cachePath);
|
|
226
|
+
if (!buffer.length) return new SQL.Database();
|
|
227
|
+
try {
|
|
228
|
+
return new SQL.Database(buffer);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
quarantineMalformedCache(cachePath, error);
|
|
231
|
+
return new SQL.Database();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function quarantineMalformedCache(cachePath, error) {
|
|
236
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
237
|
+
const corruptPath = `${cachePath}.corrupt-${stamp}-${process.pid}`;
|
|
238
|
+
try {
|
|
239
|
+
fs.renameSync(cachePath, corruptPath);
|
|
240
|
+
console.warn(`[ctx] Embedding cache was malformed and has been moved to ${corruptPath}: ${error?.message || error}`);
|
|
241
|
+
} catch {
|
|
242
|
+
try {
|
|
243
|
+
fs.rmSync(cachePath, { force: true });
|
|
244
|
+
console.warn(`[ctx] Embedding cache was malformed and has been reset: ${error?.message || error}`);
|
|
245
|
+
} catch {
|
|
246
|
+
// The caller will recreate the cache if the old file could not be moved.
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function writeDatabaseAtomically(cachePath, db) {
|
|
252
|
+
const tmpPath = `${cachePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
253
|
+
fs.writeFileSync(tmpPath, Buffer.from(db.export()));
|
|
254
|
+
fs.renameSync(tmpPath, cachePath);
|
|
255
|
+
}
|
|
256
|
+
|
|
203
257
|
async function getSql() {
|
|
204
258
|
if (!sqlPromise) {
|
|
205
259
|
sqlPromise = (async () => {
|
|
@@ -196,6 +196,7 @@ export function readProjectMcpJsonServers({ cwd = process.cwd(), configPath = pa
|
|
|
196
196
|
const mcpServers = config.mcpServers && typeof config.mcpServers === "object" ? config.mcpServers : {};
|
|
197
197
|
return Object.entries(mcpServers)
|
|
198
198
|
.filter(([, server]) => server && typeof server.command === "string")
|
|
199
|
+
.filter(([, server]) => isRunnableMcpCommand(server.command))
|
|
199
200
|
.map(([name, server]) => ({
|
|
200
201
|
name,
|
|
201
202
|
command: server.command,
|
|
@@ -203,6 +204,11 @@ export function readProjectMcpJsonServers({ cwd = process.cwd(), configPath = pa
|
|
|
203
204
|
}));
|
|
204
205
|
}
|
|
205
206
|
|
|
207
|
+
function isRunnableMcpCommand(command) {
|
|
208
|
+
if (!path.isAbsolute(command)) return true;
|
|
209
|
+
return fs.existsSync(command);
|
|
210
|
+
}
|
|
211
|
+
|
|
206
212
|
function mergeMcpServers(...groups) {
|
|
207
213
|
const merged = new Map();
|
|
208
214
|
for (const group of groups) {
|
|
@@ -253,12 +259,21 @@ function writeJsonFile(filePath, value) {
|
|
|
253
259
|
}
|
|
254
260
|
|
|
255
261
|
export function syncAntigravityMcpFromRuler({ tomlPath, configPaths = antigravityMcpConfigPaths(), dryRun = false } = {}) {
|
|
256
|
-
const
|
|
257
|
-
|
|
262
|
+
const allServers = readRulerMcpServers({ tomlPath });
|
|
263
|
+
const servers = allServers.filter((server) => isRunnableMcpCommand(server.command));
|
|
264
|
+
const skipped = allServers.filter((server) => !isRunnableMcpCommand(server.command)).map((server) => server.name);
|
|
265
|
+
if (!servers.length && !skipped.length) return { changed: false, servers: [], skipped, removed: [], configPaths };
|
|
258
266
|
|
|
267
|
+
const removed = [];
|
|
259
268
|
for (const configPath of configPaths) {
|
|
260
269
|
const config = readJsonFile(configPath, {});
|
|
261
270
|
if (!config.mcpServers || typeof config.mcpServers !== "object") config.mcpServers = {};
|
|
271
|
+
for (const [name, server] of Object.entries(config.mcpServers)) {
|
|
272
|
+
if (server?.command && !isRunnableMcpCommand(server.command)) {
|
|
273
|
+
delete config.mcpServers[name];
|
|
274
|
+
removed.push(name);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
262
277
|
for (const server of servers) {
|
|
263
278
|
config.mcpServers[server.name] = {
|
|
264
279
|
command: server.command,
|
|
@@ -268,7 +283,7 @@ export function syncAntigravityMcpFromRuler({ tomlPath, configPaths = antigravit
|
|
|
268
283
|
if (!dryRun) writeJsonFile(configPath, config);
|
|
269
284
|
}
|
|
270
285
|
|
|
271
|
-
return { changed: true, servers: servers.map((server) => server.name), configPaths };
|
|
286
|
+
return { changed: true, servers: servers.map((server) => server.name), skipped, removed: [...new Set(removed)], configPaths };
|
|
272
287
|
}
|
|
273
288
|
|
|
274
289
|
export function buildCtxMcpToml({ mcpServerPath, agents = DEFAULT_AGENTS } = {}) {
|