patchcord 0.6.24 → 0.6.26

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "patchcord",
3
3
  "description": "Cross-machine agent messaging. Messages from other agents land in the inbox and wake the agent to reply.",
4
- "version": "0.6.24",
4
+ "version": "0.6.26",
5
5
  "author": {
6
6
  "name": "ppravdin"
7
7
  },
package/bin/patchcord.mjs CHANGED
@@ -257,11 +257,14 @@ function _purgeLegacyKimiProjectConfig(dir) {
257
257
  } catch {}
258
258
  }
259
259
 
260
- // Cursor slash menu duplicates come from (a) stale combined/no-hyphen dirs under
261
- // skills-cursor and (b) Claude colon-named plugin skills bundled inside
262
- // cursor-agent's node_modules/patchcord. NEVER touch ~/.agents/skills — that tree
263
- // is Codex/Antigravity-owned (global + per-project); Cursor update must not
264
- // delete other harnesses' skills.
260
+ // Cursor slash menu duplicates come from multiple trees Cursor CLI indexes:
261
+ // (a) stale no-hyphen / colon dirs under skills-cursor
262
+ // (b) cursor-agent's bundled node_modules/patchcord both skills/ (Claude
263
+ // colon names /patchcordinbox) AND per-project-skills/cursor (exact
264
+ // dupes of skills-cursor /patchcord-inbox)
265
+ // (c) ~/.agents/skills Codex copies (/patchcord) — Cursor indexes Agent Skills
266
+ // NEVER delete ~/.agents/skills content (Codex owns that tree). We only strip
267
+ // the cursor-agent-bundled copies Cursor should not show.
265
268
  function _purgeCursorSkillDuplicates() {
266
269
  const staleCursorOnly = [
267
270
  "patchcord",
@@ -278,8 +281,22 @@ function _purgeCursorSkillDuplicates() {
278
281
  if (!existsSync(d)) continue;
279
282
  try { rmSync(d, { recursive: true, force: true }); } catch {}
280
283
  }
281
- // cursor-agent ships patchcord@* with Claude plugin skills/ Cursor must not
282
- // index those; only per-project skills-cursor copies are valid for Cursor CLI.
284
+ // Keep ONLY the three hyphenated Cursor skills under skills-cursor.
285
+ // Anything else named patchcord* there is leftover junk.
286
+ if (existsSync(cursorSkillsRoot)) {
287
+ const keep = new Set(["patchcord-inbox", "patchcord-wait", "patchcord-subscribe"]);
288
+ try {
289
+ for (const ent of readdirSync(cursorSkillsRoot, { withFileTypes: true })) {
290
+ if (!ent.isDirectory()) continue;
291
+ if (!/^patchcord/i.test(ent.name)) continue;
292
+ if (keep.has(ent.name)) continue;
293
+ try { rmSync(join(cursorSkillsRoot, ent.name), { recursive: true, force: true }); } catch {}
294
+ }
295
+ } catch {}
296
+ }
297
+ // cursor-agent ships the full npm package. Cursor indexes SKILL.md under it,
298
+ // so strip BOTH skills/ and per-project-skills/ from every bundled copy.
299
+ // Canonical Cursor skills live only in ~/.cursor/skills-cursor/.
283
300
  const cursorAgentRoot = join(HOME, ".local", "share", "cursor-agent");
284
301
  if (existsSync(cursorAgentRoot)) {
285
302
  const walk = (dir, depth = 0) => {
@@ -290,8 +307,8 @@ function _purgeCursorSkillDuplicates() {
290
307
  const p = join(dir, ent.name);
291
308
  if (ent.isDirectory()) {
292
309
  if (ent.name === "patchcord" && dir.endsWith("node_modules")) {
293
- for (const pluginSkill of ["inbox", "wait", "subscribe"]) {
294
- const sd = join(p, "skills", pluginSkill);
310
+ for (const sub of ["skills", "per-project-skills"]) {
311
+ const sd = join(p, sub);
295
312
  if (existsSync(sd)) {
296
313
  try { rmSync(sd, { recursive: true, force: true }); } catch {}
297
314
  }
@@ -318,8 +335,10 @@ function _purgeCursorSkillDuplicates() {
318
335
  }
319
336
  }
320
337
 
321
- // Codex global skills live at ~/.agents/skills/ (not Cursor). Refresh on
322
- // update so a mistaken Cursor-only purge cannot leave Codex without @patchcord.
338
+ // Codex global skills live at ~/.agents/skills/. Cursor CLI also indexes that
339
+ // tree (no exclude setting see cursor.com/docs/skills). Use ONE hyphenated
340
+ // wait skill here so Cursor does not also show /patchcordwait (colon) +
341
+ // /patchcord-wait (skills-cursor). Claude plugin skills keep colon names.
323
342
  function _restoreCodexGlobalSkills() {
324
343
  const codexConfig = join(HOME, ".codex", "config.toml");
325
344
  if (!existsSync(codexConfig)) return;
@@ -330,11 +349,20 @@ function _restoreCodexGlobalSkills() {
330
349
  writeFileSync(join(globalSkillDir, "SKILL.md"),
331
350
  readFileSync(join(pluginRoot, "per-project-skills", "codex", "SKILL.md"), "utf-8"));
332
351
  mkdirSync(globalWaitDir, { recursive: true });
352
+ // Shared hyphenated wait (same MCP wait_for_message flow as Cursor).
333
353
  writeFileSync(join(globalWaitDir, "SKILL.md"),
334
- readFileSync(join(pluginRoot, "skills", "wait", "SKILL.md"), "utf-8"));
354
+ readFileSync(join(pluginRoot, "per-project-skills", "cursor", "wait", "SKILL.md"), "utf-8"));
335
355
  } catch {}
336
356
  }
337
357
 
358
+ /** True when Codex is on this machine (agents skills would be indexed by Cursor). */
359
+ function _hasCodexAgentsSkills() {
360
+ if (existsSync(join(HOME, ".codex", "config.toml"))) return true;
361
+ if (existsSync(join(HOME, ".agents", "skills", "patchcord"))) return true;
362
+ if (existsSync(join(HOME, ".agents", "skills", "patchcord-wait"))) return true;
363
+ return false;
364
+ }
365
+
338
366
  // preserving the rest of the user's YAML. Block-style only (what Hermes writes).
339
367
  function upsertHermesConfig(existing, url, token) {
340
368
  const block = [
@@ -1949,16 +1977,28 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1949
1977
  if (existsSync(staleCursorSkillDir)) {
1950
1978
  try { rmSync(staleCursorSkillDir, { recursive: true, force: true }); cursorChanged = true; } catch {}
1951
1979
  }
1980
+ // Always install Cursor-specific inbox + subscribe (not in ~/.agents).
1952
1981
  mkdirSync(cursorInboxDir, { recursive: true });
1953
1982
  cpSync(join(pluginRoot, "per-project-skills", "cursor", "inbox", "SKILL.md"), join(cursorInboxDir, "SKILL.md"));
1954
1983
  cursorChanged = true;
1955
- mkdirSync(cursorWaitDir, { recursive: true });
1956
- cpSync(join(pluginRoot, "per-project-skills", "cursor", "wait", "SKILL.md"), join(cursorWaitDir, "SKILL.md"));
1957
1984
  mkdirSync(cursorSubscribeDir, { recursive: true });
1958
1985
  cpSync(
1959
1986
  join(pluginRoot, "per-project-skills", "cursor", "subscribe", "SKILL.md"),
1960
1987
  join(cursorSubscribeDir, "SKILL.md")
1961
1988
  );
1989
+ // Wait is shared MCP flow. If Codex/agents already owns patchcord-wait,
1990
+ // keep ONE copy under ~/.agents (Cursor indexes it) — do NOT also install
1991
+ // skills-cursor/patchcord-wait (duplicate slash). Cursor-only machines
1992
+ // still get wait under skills-cursor.
1993
+ if (_hasCodexAgentsSkills()) {
1994
+ _restoreCodexGlobalSkills();
1995
+ if (existsSync(cursorWaitDir)) {
1996
+ try { rmSync(cursorWaitDir, { recursive: true, force: true }); cursorChanged = true; } catch {}
1997
+ }
1998
+ } else {
1999
+ mkdirSync(cursorWaitDir, { recursive: true });
2000
+ cpSync(join(pluginRoot, "per-project-skills", "cursor", "wait", "SKILL.md"), join(cursorWaitDir, "SKILL.md"));
2001
+ }
1962
2002
  if (cursorChanged) globalChanges.push("Cursor skills installed");
1963
2003
 
1964
2004
  if (hasCursorAgent) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.6.24",
3
+ "version": "0.6.26",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "scripts": {
6
6
  "version": "node scripts/sync-plugin-version.mjs && git add .claude-plugin/plugin.json"