@danielblomma/cortex-mcp 2.0.12 → 2.0.13
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danielblomma/cortex-mcp",
|
|
3
3
|
"mcpName": "io.github.DanielBlomma/cortex",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.13",
|
|
5
5
|
"description": "Local, repo-scoped context platform for coding assistants. Semantic search, graph relationships, and architectural rule context.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": "Daniel Blomma",
|
|
@@ -46,6 +46,7 @@ type ManifestEntry = {
|
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
type LocalSkillRecord = {
|
|
49
|
+
cli: SkillCli;
|
|
49
50
|
scope: string;
|
|
50
51
|
updated_at: string;
|
|
51
52
|
path: string;
|
|
@@ -88,7 +89,29 @@ function readState(): LocalSkillsState {
|
|
|
88
89
|
if (!existsSync(path)) return { skills: {} };
|
|
89
90
|
try {
|
|
90
91
|
const parsed = JSON.parse(readFileSync(path, "utf8")) as LocalSkillsState;
|
|
91
|
-
|
|
92
|
+
const normalizedSkills: Record<string, LocalSkillRecord> = {};
|
|
93
|
+
for (const [key, record] of Object.entries(parsed.skills ?? {})) {
|
|
94
|
+
if (!record || typeof record !== "object") continue;
|
|
95
|
+
const inferredCli =
|
|
96
|
+
record.path?.includes("/.codex/skills/")
|
|
97
|
+
? "codex"
|
|
98
|
+
: "claude";
|
|
99
|
+
const cli =
|
|
100
|
+
record.cli === "codex" || record.cli === "claude"
|
|
101
|
+
? record.cli
|
|
102
|
+
: inferredCli;
|
|
103
|
+
const normalizedKey = key.includes(":") ? key : `${cli}:${key}`;
|
|
104
|
+
normalizedSkills[normalizedKey] = {
|
|
105
|
+
cli,
|
|
106
|
+
scope: String(record.scope ?? "global"),
|
|
107
|
+
updated_at: String(record.updated_at ?? ""),
|
|
108
|
+
path: String(record.path ?? ""),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
skills: normalizedSkills,
|
|
113
|
+
last_synced_at: parsed.last_synced_at,
|
|
114
|
+
};
|
|
92
115
|
} catch {
|
|
93
116
|
return { skills: {} };
|
|
94
117
|
}
|
|
@@ -103,19 +126,22 @@ function writeState(state: LocalSkillsState): void {
|
|
|
103
126
|
}
|
|
104
127
|
|
|
105
128
|
/**
|
|
106
|
-
* Resolve the on-disk SKILL.md path for a skill. Global
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* Claude-only and lands in ~/.claude/skills.
|
|
129
|
+
* Resolve the on-disk SKILL.md path for a skill install target. Global
|
|
130
|
+
* skills are installed once per CLI, so the destination root depends on the
|
|
131
|
+
* active sync target rather than just the stored scope.
|
|
110
132
|
*/
|
|
111
|
-
function skillFilePath(
|
|
133
|
+
function skillFilePath(cli: SkillCli, name: string): string {
|
|
112
134
|
const root =
|
|
113
|
-
|
|
135
|
+
cli === "codex"
|
|
114
136
|
? join(homedir(), ".codex", "skills")
|
|
115
137
|
: join(homedir(), ".claude", "skills");
|
|
116
138
|
return join(root, name, "SKILL.md");
|
|
117
139
|
}
|
|
118
140
|
|
|
141
|
+
function stateSkillKey(cli: SkillCli, name: string): string {
|
|
142
|
+
return `${cli}:${name}`;
|
|
143
|
+
}
|
|
144
|
+
|
|
119
145
|
function shouldSyncForCli(scope: string, cli: SkillCli): boolean {
|
|
120
146
|
if (scope === "global") return true;
|
|
121
147
|
return scope === `cli:${cli}`;
|
|
@@ -222,7 +248,8 @@ export async function runSkillSyncForCli(
|
|
|
222
248
|
|
|
223
249
|
// Detect adds + changes
|
|
224
250
|
for (const entry of relevantManifest) {
|
|
225
|
-
const
|
|
251
|
+
const skillKey = stateSkillKey(cli, entry.name);
|
|
252
|
+
const local = state.skills[skillKey];
|
|
226
253
|
const isNew = !local;
|
|
227
254
|
const isChanged =
|
|
228
255
|
Boolean(local) &&
|
|
@@ -243,7 +270,7 @@ export async function runSkillSyncForCli(
|
|
|
243
270
|
};
|
|
244
271
|
}
|
|
245
272
|
|
|
246
|
-
const path = skillFilePath(
|
|
273
|
+
const path = skillFilePath(cli, entry.name);
|
|
247
274
|
try {
|
|
248
275
|
writeSkillFile(path, body);
|
|
249
276
|
} catch (err) {
|
|
@@ -257,7 +284,8 @@ export async function runSkillSyncForCli(
|
|
|
257
284
|
};
|
|
258
285
|
}
|
|
259
286
|
|
|
260
|
-
state.skills[
|
|
287
|
+
state.skills[skillKey] = {
|
|
288
|
+
cli,
|
|
261
289
|
scope: entry.scope,
|
|
262
290
|
updated_at: entry.updated_at,
|
|
263
291
|
path,
|
|
@@ -269,7 +297,10 @@ export async function runSkillSyncForCli(
|
|
|
269
297
|
// dropped (or disabled). We only consider state entries whose scope
|
|
270
298
|
// matches this cli, so we don't accidentally remove the other CLI's
|
|
271
299
|
// skills when running a per-cli tick.
|
|
272
|
-
for (const [
|
|
300
|
+
for (const [skillKey, record] of Object.entries(state.skills)) {
|
|
301
|
+
if (record.cli !== cli) continue;
|
|
302
|
+
const [, name] = skillKey.split(":", 2);
|
|
303
|
+
if (!name) continue;
|
|
273
304
|
if (!shouldSyncForCli(record.scope, cli)) continue;
|
|
274
305
|
if (remoteByName.has(name)) continue;
|
|
275
306
|
try {
|
|
@@ -277,7 +308,7 @@ export async function runSkillSyncForCli(
|
|
|
277
308
|
} catch {
|
|
278
309
|
// best-effort; if unlink fails the next tick will retry
|
|
279
310
|
}
|
|
280
|
-
delete state.skills[
|
|
311
|
+
delete state.skills[skillKey];
|
|
281
312
|
removed.push(name);
|
|
282
313
|
}
|
|
283
314
|
|