patchcord 0.6.20 → 0.6.22

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.20",
4
+ "version": "0.6.22",
5
5
  "author": {
6
6
  "name": "ppravdin"
7
7
  },
package/bin/patchcord.mjs CHANGED
@@ -43,77 +43,6 @@ function isSafeId(s) {
43
43
  return /^[A-Za-z0-9_\-]+$/.test(s) && s.length < 100;
44
44
  }
45
45
 
46
- /** Claude Code marketplace plugin — call ONLY from the Claude provision branch. */
47
- function _installClaudeMarketplacePlugin(stablePluginDir) {
48
- if (!run("which claude")) return;
49
- const npmCachePatchcord = join(HOME, ".claude", "plugins", "npm-cache", "node_modules", "patchcord");
50
- if (existsSync(npmCachePatchcord)) {
51
- try { rmSync(npmCachePatchcord, { recursive: true, force: true }); } catch {}
52
- }
53
- run(`claude plugin marketplace remove patchcord-marketplace`);
54
- run(`claude plugin marketplace add "${stablePluginDir}"`);
55
- const hadPlugin = run(`claude plugin list`)?.includes("patchcord");
56
- if (hadPlugin) {
57
- run(`claude plugin update patchcord@patchcord-marketplace`);
58
- } else {
59
- run(`claude plugin install patchcord@patchcord-marketplace`);
60
- }
61
- return hadPlugin;
62
- }
63
-
64
- /** Undo 0.6.14 mistake: Claude Code uses /mcp (not /mcp/bearer). */
65
- function revertClaudeMcpBearerUrl(mcpPath) {
66
- if (!existsSync(mcpPath)) return false;
67
- try {
68
- const obj = JSON.parse(readFileSync(mcpPath, "utf-8"));
69
- const pt = obj?.mcpServers?.patchcord;
70
- const url = pt?.url;
71
- if (!url || !url.endsWith("/mcp/bearer")) return false;
72
- pt.url = url.replace(/\/mcp\/bearer$/, "/mcp");
73
- writeFileSync(mcpPath, JSON.stringify(obj, null, 2) + "\n");
74
- return true;
75
- } catch {
76
- return false;
77
- }
78
- }
79
-
80
- /** Patchcord uses project .mcp.json only — drop stale local-scope duplicates. */
81
- function dedupeClaudePatchcordMcp(cwd) {
82
- if (run("which claude")) {
83
- run("claude mcp remove patchcord -s local");
84
- }
85
- const claudeJson = join(HOME, ".claude.json");
86
- if (!existsSync(claudeJson)) return false;
87
- try {
88
- const obj = JSON.parse(readFileSync(claudeJson, "utf-8"));
89
- let changed = false;
90
- if (obj.mcpServers?.patchcord) {
91
- delete obj.mcpServers.patchcord;
92
- if (Object.keys(obj.mcpServers).length === 0) delete obj.mcpServers;
93
- changed = true;
94
- }
95
- const proj = obj.projects?.[cwd];
96
- if (proj?.mcpServers?.patchcord) {
97
- delete proj.mcpServers.patchcord;
98
- if (Object.keys(proj.mcpServers).length === 0) delete proj.mcpServers;
99
- changed = true;
100
- }
101
- if (changed) {
102
- writeFileSync(claudeJson, JSON.stringify(obj, null, 2) + "\n");
103
- }
104
- return changed;
105
- } catch {
106
- return false;
107
- }
108
- }
109
-
110
- function fixClaudePatchcordMcp(cwd) {
111
- const mcpPath = join(cwd, ".mcp.json");
112
- const reverted = revertClaudeMcpBearerUrl(mcpPath);
113
- const deduped = dedupeClaudePatchcordMcp(cwd);
114
- return { reverted, deduped };
115
- }
116
-
117
46
  // A trailing --help/-h on ANY subcommand must show help and exit — never run the
118
47
  // command. (patchcord orchestrator --help used to PROVISION an orchestrator;
119
48
  // patchcord login --help hung on the browser login. Side-effecting --help is a
@@ -779,8 +708,6 @@ if (cmd === "subscribe") {
779
708
  const forceKimi = args.includes("--kimi");
780
709
  const intervalArg = args.find((a) => a !== "--kimi") || "30";
781
710
 
782
- fixClaudePatchcordMcp(process.cwd());
783
-
784
711
  // Kimi Code uses polling instead of WebSocket realtime.
785
712
  // Force Kimi mode with --kimi flag, or auto-detect from bearer config.
786
713
  let isKimi = forceKimi;
@@ -1734,8 +1661,6 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1734
1661
  return out.replace(/,(\s*[}\]])/g, "$1");
1735
1662
  }
1736
1663
 
1737
- // dedupeClaudePatchcordMcp + revertClaudeMcpBearerUrl live at module scope.
1738
-
1739
1664
  // Read+merge+write a JSON config file. If the file exists but its contents
1740
1665
  // can't be parsed, REFUSE to write — silently overwriting would erase
1741
1666
  // unrelated MCP servers, settings, or hand-edits the user has in there.
@@ -1794,15 +1719,6 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1794
1719
  const bold = "\x1b[1m";
1795
1720
  const r = "\x1b[0m";
1796
1721
 
1797
- const _installCwd = process.cwd();
1798
- const _mcpFix = fixClaudePatchcordMcp(_installCwd);
1799
- if (_mcpFix.reverted) {
1800
- console.log(` ${green}✓${r} Claude Code MCP URL reverted to /mcp (0.6.14 /mcp/bearer was wrong for CC)`);
1801
- }
1802
- if (_mcpFix.deduped) {
1803
- console.log(` ${green}✓${r} Removed stale local-scope patchcord MCP (use project .mcp.json only)`);
1804
- }
1805
-
1806
1722
  // ── Purge stale npx cache entries ────────────────────────────
1807
1723
  // npx never auto-updates a cached package. Users with an old `npx patchcord`
1808
1724
  // entry get the stale binary indefinitely. On every install we sweep
@@ -1868,11 +1784,33 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1868
1784
  let globalChanges = [];
1869
1785
  if (globalCliInstalled) globalChanges.push("Patchcord CLI installed globally");
1870
1786
 
1871
- // Claude Code — skip when install runs inside cursor-agent: Claude's plugin
1872
- // MCP registers as "plugin-patchcord-marketplace-patchcord", which cursor-agent
1873
- // tries instead of project .cursor/mcp.json → "MCP server not found".
1787
+ // Claude Code
1874
1788
  const hasClaude = run("which claude");
1875
1789
  if (hasClaude) {
1790
+ // Remove legacy npm-cache install (pre-marketplace era) — causes duplicate /patchcord commands.
1791
+ const npmCachePatchcord = join(HOME, ".claude", "plugins", "npm-cache", "node_modules", "patchcord");
1792
+ if (existsSync(npmCachePatchcord)) {
1793
+ try { rmSync(npmCachePatchcord, { recursive: true, force: true }); } catch {}
1794
+ }
1795
+
1796
+ // Always re-add marketplace (point at the STABLE mirror, not pluginRoot
1797
+ // which is the npx ephemeral cache). Claude Code stores the literal
1798
+ // path in its plugin manifest — pointing at npx cache breaks every
1799
+ // future session as soon as that cache dir is swept.
1800
+ // Remove any prior marketplace registration first so the path actually
1801
+ // updates for users who got the buggy npx-cache path on an earlier install.
1802
+ run(`claude plugin marketplace remove patchcord-marketplace`);
1803
+ run(`claude plugin marketplace add "${stablePluginDir}"`);
1804
+ const installed = run(`claude plugin list`)?.includes("patchcord");
1805
+ wasPluginInstalled = !!installed;
1806
+ if (installed) {
1807
+ run(`claude plugin update patchcord@patchcord-marketplace`);
1808
+ globalChanges.push("Claude Code plugin updated");
1809
+ } else {
1810
+ run(`claude plugin install patchcord@patchcord-marketplace`);
1811
+ globalChanges.push("Claude Code plugin installed");
1812
+ }
1813
+
1876
1814
  const claudeSettings = join(HOME, ".claude", "settings.json");
1877
1815
  if (existsSync(claudeSettings)) {
1878
1816
  try {
@@ -1963,9 +1901,8 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1963
1901
  globalChanges.push("Cursor MCP allowlist configured");
1964
1902
  }
1965
1903
 
1966
- // cursor-agent v2026.07+ also reads ~/.cursor/cli-config.json permissions.allow
1967
- // for MCP tool approval — permissions.json alone leaves patchcord at
1968
- // "not loaded (needs approval)" even with --yolo.
1904
+ // cursor-agent v2026.07+ reads ~/.cursor/cli-config.json permissions.allow
1905
+ // for MCP — permissions.json mcpAllowlist alone leaves patchcord unloaded.
1969
1906
  const cliConfigPath = join(HOME, ".cursor", "cli-config.json");
1970
1907
  let cliAllowChanged = false;
1971
1908
  const cliOk = updateJsonConfig(cliConfigPath, (obj) => {
@@ -2423,9 +2360,8 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
2423
2360
  }
2424
2361
  toolSlug = toolFlag; // preserved as-is for the URL param
2425
2362
  }
2426
- // cursor-agent --yolo runs `npx patchcord` without --tool=cursor. Without
2427
- // this, browser connect defaults to Claude and writes .mcp.json instead of
2428
- // .cursor/mcp.json — MCP never loads in cursor-agent.
2363
+ // cursor-agent --yolo runs npx patchcord without --tool=cursor; auto-select
2364
+ // Cursor so provision writes .cursor/mcp.json for this harness.
2429
2365
  if (!choice && process.env.CURSOR_AGENT) {
2430
2366
  choice = CLIENT_TYPE_MAP.cursor;
2431
2367
  toolSlug = toolSlug || "cursor";
@@ -2834,7 +2770,6 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
2834
2770
  console.log(`\n ${green}✓${r} Cursor configured: ${dim}${cursorPath}${r}`);
2835
2771
  console.log(` ${dim}Per-project only — other projects won't see this agent.${r}`);
2836
2772
  }
2837
- // Project-layer CLI permissions (cursor-agent merges .cursor/cli.json from git root).
2838
2773
  const cursorCliPath = join(cursorDir, "cli.json");
2839
2774
  const cursorCliOk = updateJsonConfig(cursorCliPath, (obj) => {
2840
2775
  obj.permissions = obj.permissions || {};
@@ -3387,14 +3322,8 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
3387
3322
  // their JSON and re-runs.
3388
3323
  process.exit(1);
3389
3324
  }
3390
- dedupeClaudePatchcordMcp(cwd);
3391
3325
  console.log(`\n ${green}✓${r} Claude Code configured: ${dim}${mcpPath}${r}`);
3392
3326
 
3393
- if (run("which claude")) {
3394
- wasPluginInstalled = _installClaudeMarketplacePlugin(stablePluginDir);
3395
- console.log(` ${green}✓${r} Claude Code plugin ${wasPluginInstalled ? "updated" : "installed"}`);
3396
- }
3397
-
3398
3327
  // Statusline: ask whether to enable full mode (unless --full flag passed or already configured).
3399
3328
  const enableScript = join(pluginRoot, "scripts", "enable-statusline.sh");
3400
3329
  if (existsSync(enableScript)) {
@@ -3504,13 +3433,12 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
3504
3433
 
3505
3434
  if (isOpenClaw) {
3506
3435
  console.log(`\n${dim}Run${r} ${bold}openclaw gateway restart${r}${dim}, then tools will be available in your channels.${r}`);
3436
+ } else if (isCursor && hasCursorAgent) {
3437
+ console.log(`\n ${green}→${r} ${bold}Exit and start a new ${cyan}cursor-agent${r}${bold} session${r} ${dim}(MCP loads at session start)${r}`);
3507
3438
  } else if (isKimiCode) {
3508
3439
  console.log(`\n ${green}→${r} ${bold}Restart ${cyan}kimi${r}${bold} in this project (or run ${cyan}/reload${r}${bold}), then say: ${cyan}${bold}check inbox${r}`);
3509
3440
  } else if (isKimi) {
3510
3441
  console.log(`\n ${green}→${r} ${bold}Restart your ${toolName} session with ${cyan}kimi-pc${r}${bold}, then say: ${cyan}${bold}check inbox${r}`);
3511
- } else if (isCursor && hasCursorAgent) {
3512
- console.log(`\n ${green}→${r} ${bold}Exit and start a new ${cyan}cursor-agent${r}${bold} session${r} ${dim}(MCP loads at session start — not mid-session)${r}`);
3513
- console.log(` ${dim}Then:${r} ${cyan}${bold}check inbox${r} ${dim}before${r} ${cyan}${bold}/patchcord-subscribe${r}`);
3514
3442
  } else {
3515
3443
  console.log(`\n ${green}→${r} ${bold}Restart your ${toolName} session${r}, then say: ${cyan}${bold}check inbox${r}`);
3516
3444
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.6.20",
3
+ "version": "0.6.22",
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"