patchcord 0.6.12 → 0.6.14

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.12",
4
+ "version": "0.6.14",
5
5
  "author": {
6
6
  "name": "ppravdin"
7
7
  },
package/bin/patchcord.mjs CHANGED
@@ -177,16 +177,14 @@ function _purgeLegacyKimiProjectConfig(dir) {
177
177
  } catch {}
178
178
  }
179
179
 
180
- // Cursor indexes skills from multiple trees. Leftover Codex globals
181
- // (~/.agents/skills/patchcord*) and Claude colon-named plugin skills bundled
182
- // inside cursor-agent's node_modules/patchcord both register duplicate slash
183
- // commands (/patchcordinbox vs /patchcord-inbox). Keep only skills-cursor.
180
+ // Cursor slash menu duplicates come from (a) stale combined/no-hyphen dirs under
181
+ // skills-cursor and (b) Claude colon-named plugin skills bundled inside
182
+ // cursor-agent's node_modules/patchcord. NEVER touch ~/.agents/skills — that tree
183
+ // is Codex/Antigravity-owned (global + per-project); Cursor update must not
184
+ // delete other harnesses' skills.
184
185
  function _purgeCursorSkillDuplicates() {
185
- const staleNames = [
186
+ const staleCursorOnly = [
186
187
  "patchcord",
187
- "patchcord-inbox",
188
- "patchcord-wait",
189
- "patchcord-subscribe",
190
188
  "patchcordinbox",
191
189
  "patchcordwait",
192
190
  "patchcordsubscribe",
@@ -194,15 +192,11 @@ function _purgeCursorSkillDuplicates() {
194
192
  "patchcord:wait",
195
193
  "patchcord:subscribe",
196
194
  ];
197
- for (const root of [
198
- join(HOME, ".agents", "skills"),
199
- join(HOME, ".cursor", "skills-cursor"),
200
- ]) {
201
- for (const name of staleNames) {
202
- const d = join(root, name);
203
- if (!existsSync(d)) continue;
204
- try { rmSync(d, { recursive: true, force: true }); } catch {}
205
- }
195
+ const cursorSkillsRoot = join(HOME, ".cursor", "skills-cursor");
196
+ for (const name of staleCursorOnly) {
197
+ const d = join(cursorSkillsRoot, name);
198
+ if (!existsSync(d)) continue;
199
+ try { rmSync(d, { recursive: true, force: true }); } catch {}
206
200
  }
207
201
  // cursor-agent ships patchcord@* with Claude plugin skills/ — Cursor must not
208
202
  // index those; only per-project skills-cursor copies are valid for Cursor CLI.
@@ -244,6 +238,23 @@ function _purgeCursorSkillDuplicates() {
244
238
  }
245
239
  }
246
240
 
241
+ // Codex global skills live at ~/.agents/skills/ (not Cursor). Refresh on
242
+ // update so a mistaken Cursor-only purge cannot leave Codex without @patchcord.
243
+ function _restoreCodexGlobalSkills() {
244
+ const codexConfig = join(HOME, ".codex", "config.toml");
245
+ if (!existsSync(codexConfig)) return;
246
+ const globalSkillDir = join(HOME, ".agents", "skills", "patchcord");
247
+ const globalWaitDir = join(HOME, ".agents", "skills", "patchcord-wait");
248
+ try {
249
+ mkdirSync(globalSkillDir, { recursive: true });
250
+ writeFileSync(join(globalSkillDir, "SKILL.md"),
251
+ readFileSync(join(pluginRoot, "per-project-skills", "codex", "SKILL.md"), "utf-8"));
252
+ mkdirSync(globalWaitDir, { recursive: true });
253
+ writeFileSync(join(globalWaitDir, "SKILL.md"),
254
+ readFileSync(join(pluginRoot, "skills", "wait", "SKILL.md"), "utf-8"));
255
+ } catch {}
256
+ }
257
+
247
258
  // preserving the rest of the user's YAML. Block-style only (what Hermes writes).
248
259
  function upsertHermesConfig(existing, url, token) {
249
260
  const block = [
@@ -1161,9 +1172,13 @@ if (cmd === "login" || cmd === "orchestrator" || cmd === "teamlead" || cmd === "
1161
1172
  // default: claude_code. type:"http" is REQUIRED — without it Claude Code
1162
1173
  // defaults to stdio transport and rejects the entry ("command: expected
1163
1174
  // string, received undefined").
1175
+ // Use /mcp/bearer (not /mcp): Claude Code v2.1.113+ follows RFC 9728 OAuth
1176
+ // discovery on /mcp, drops the static bearer header, and stalls on "Needs
1177
+ // authentication". /mcp/bearer serves bearer-only resource metadata — same
1178
+ // path Cursor and Kimi Code already use.
1164
1179
  return writeJson(join(dir, ".mcp.json"), (o) => {
1165
1180
  o.mcpServers = o.mcpServers || {};
1166
- o.mcpServers.patchcord = { type: "http", url: `${baseUrl}/mcp`, headers: hdr };
1181
+ o.mcpServers.patchcord = { type: "http", url: `${baseUrl}/mcp/bearer`, headers: hdr };
1167
1182
  });
1168
1183
  };
1169
1184
 
@@ -1650,6 +1665,23 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1650
1665
  return out.replace(/,(\s*[}\]])/g, "$1");
1651
1666
  }
1652
1667
 
1668
+ // Claude Code v2.1.113+ OAuth discovery on plain /mcp breaks bearer MCP.
1669
+ // Rewrite legacy project configs to /mcp/bearer (Cursor/Kimi already use it).
1670
+ function _migrateClaudeMcpBearerUrl(mcpPath) {
1671
+ if (!existsSync(mcpPath)) return false;
1672
+ try {
1673
+ const obj = JSON.parse(readFileSync(mcpPath, "utf-8"));
1674
+ const pt = obj?.mcpServers?.patchcord;
1675
+ const url = pt?.url;
1676
+ if (!url || !/\/mcp$/.test(url) || url.endsWith("/mcp/bearer")) return false;
1677
+ pt.url = url.replace(/\/mcp$/, "/mcp/bearer");
1678
+ writeFileSync(mcpPath, JSON.stringify(obj, null, 2) + "\n");
1679
+ return true;
1680
+ } catch {
1681
+ return false;
1682
+ }
1683
+ }
1684
+
1653
1685
  // Read+merge+write a JSON config file. If the file exists but its contents
1654
1686
  // can't be parsed, REFUSE to write — silently overwriting would erase
1655
1687
  // unrelated MCP servers, settings, or hand-edits the user has in there.
@@ -1708,6 +1740,10 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1708
1740
  const bold = "\x1b[1m";
1709
1741
  const r = "\x1b[0m";
1710
1742
 
1743
+ if (_migrateClaudeMcpBearerUrl(join(process.cwd(), ".mcp.json"))) {
1744
+ console.log(` ${green}✓${r} Claude Code MCP URL migrated to /mcp/bearer`);
1745
+ }
1746
+
1711
1747
  // ── Purge stale npx cache entries ────────────────────────────
1712
1748
  // npx never auto-updates a cached package. Users with an old `npx patchcord`
1713
1749
  // entry get the stale binary indefinitely. On every install we sweep
@@ -2249,6 +2285,9 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
2249
2285
  globalChanges.push(`Codex stop hook ${hookAlreadyExisted ? "updated" : "installed"}`);
2250
2286
  }
2251
2287
 
2288
+ _restoreCodexGlobalSkills();
2289
+ globalChanges.push("Codex global skills refreshed");
2290
+
2252
2291
  writeFileSync(codexConfig, globalCodexContent);
2253
2292
  }
2254
2293
 
@@ -3237,7 +3276,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
3237
3276
  mcpServers: {
3238
3277
  patchcord: {
3239
3278
  type: "http",
3240
- url: `${serverUrl}/mcp`,
3279
+ url: `${serverUrl}/mcp/bearer`,
3241
3280
  headers: {
3242
3281
  Authorization: `Bearer ${token}`,
3243
3282
  "X-Patchcord-Machine": hostname,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.6.12",
3
+ "version": "0.6.14",
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"