patchcord 0.6.19 → 0.6.21

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.19",
4
+ "version": "0.6.21",
5
5
  "author": {
6
6
  "name": "ppravdin"
7
7
  },
package/bin/patchcord.mjs CHANGED
@@ -43,71 +43,6 @@ function isSafeId(s) {
43
43
  return /^[A-Za-z0-9_\-]+$/.test(s) && s.length < 100;
44
44
  }
45
45
 
46
- /** cursor-agent --yolo often spawns `npx patchcord` without CURSOR_AGENT in env. */
47
- function _runningUnderCursorAgent() {
48
- if (process.env.CURSOR_AGENT) return true;
49
- if (process.platform === "linux") {
50
- try {
51
- const cmdline = readFileSync(`/proc/${process.ppid}/cmdline`, "utf-8").replace(/\0/g, " ");
52
- if (/cursor-agent|\bcursor agent\b/i.test(cmdline)) return true;
53
- } catch {}
54
- }
55
- return false;
56
- }
57
-
58
- /** Undo 0.6.14 mistake: Claude Code uses /mcp (not /mcp/bearer). */
59
- function revertClaudeMcpBearerUrl(mcpPath) {
60
- if (!existsSync(mcpPath)) return false;
61
- try {
62
- const obj = JSON.parse(readFileSync(mcpPath, "utf-8"));
63
- const pt = obj?.mcpServers?.patchcord;
64
- const url = pt?.url;
65
- if (!url || !url.endsWith("/mcp/bearer")) return false;
66
- pt.url = url.replace(/\/mcp\/bearer$/, "/mcp");
67
- writeFileSync(mcpPath, JSON.stringify(obj, null, 2) + "\n");
68
- return true;
69
- } catch {
70
- return false;
71
- }
72
- }
73
-
74
- /** Patchcord uses project .mcp.json only — drop stale local-scope duplicates. */
75
- function dedupeClaudePatchcordMcp(cwd) {
76
- if (run("which claude")) {
77
- run("claude mcp remove patchcord -s local");
78
- }
79
- const claudeJson = join(HOME, ".claude.json");
80
- if (!existsSync(claudeJson)) return false;
81
- try {
82
- const obj = JSON.parse(readFileSync(claudeJson, "utf-8"));
83
- let changed = false;
84
- if (obj.mcpServers?.patchcord) {
85
- delete obj.mcpServers.patchcord;
86
- if (Object.keys(obj.mcpServers).length === 0) delete obj.mcpServers;
87
- changed = true;
88
- }
89
- const proj = obj.projects?.[cwd];
90
- if (proj?.mcpServers?.patchcord) {
91
- delete proj.mcpServers.patchcord;
92
- if (Object.keys(proj.mcpServers).length === 0) delete proj.mcpServers;
93
- changed = true;
94
- }
95
- if (changed) {
96
- writeFileSync(claudeJson, JSON.stringify(obj, null, 2) + "\n");
97
- }
98
- return changed;
99
- } catch {
100
- return false;
101
- }
102
- }
103
-
104
- function fixClaudePatchcordMcp(cwd) {
105
- const mcpPath = join(cwd, ".mcp.json");
106
- const reverted = revertClaudeMcpBearerUrl(mcpPath);
107
- const deduped = dedupeClaudePatchcordMcp(cwd);
108
- return { reverted, deduped };
109
- }
110
-
111
46
  // A trailing --help/-h on ANY subcommand must show help and exit — never run the
112
47
  // command. (patchcord orchestrator --help used to PROVISION an orchestrator;
113
48
  // patchcord login --help hung on the browser login. Side-effecting --help is a
@@ -773,8 +708,6 @@ if (cmd === "subscribe") {
773
708
  const forceKimi = args.includes("--kimi");
774
709
  const intervalArg = args.find((a) => a !== "--kimi") || "30";
775
710
 
776
- fixClaudePatchcordMcp(process.cwd());
777
-
778
711
  // Kimi Code uses polling instead of WebSocket realtime.
779
712
  // Force Kimi mode with --kimi flag, or auto-detect from bearer config.
780
713
  let isKimi = forceKimi;
@@ -1683,11 +1616,6 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1683
1616
  const flags = cmd?.startsWith("--") ? process.argv.slice(2) : process.argv.slice(3);
1684
1617
  const fullStatusline = flags.includes("--full");
1685
1618
  const updateOnly = flags.includes("--update-only");
1686
- const toolFlagEarly = flags.find((f) => f.startsWith("--tool="))?.split("=")[1]
1687
- || (flags.includes("--tool") ? flags[flags.indexOf("--tool") + 1] : "");
1688
- const skipClaudePluginForCursor =
1689
- _runningUnderCursorAgent()
1690
- || toolFlagEarly.replace(/-/g, "_") === "cursor";
1691
1619
  let wasPluginInstalled = false;
1692
1620
  const { readFileSync, writeFileSync, unlinkSync, rmSync, chmodSync, copyFileSync } = await import("fs");
1693
1621
 
@@ -1733,8 +1661,6 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1733
1661
  return out.replace(/,(\s*[}\]])/g, "$1");
1734
1662
  }
1735
1663
 
1736
- // dedupeClaudePatchcordMcp + revertClaudeMcpBearerUrl live at module scope.
1737
-
1738
1664
  // Read+merge+write a JSON config file. If the file exists but its contents
1739
1665
  // can't be parsed, REFUSE to write — silently overwriting would erase
1740
1666
  // unrelated MCP servers, settings, or hand-edits the user has in there.
@@ -1793,15 +1719,6 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1793
1719
  const bold = "\x1b[1m";
1794
1720
  const r = "\x1b[0m";
1795
1721
 
1796
- const _installCwd = process.cwd();
1797
- const _mcpFix = fixClaudePatchcordMcp(_installCwd);
1798
- if (_mcpFix.reverted) {
1799
- console.log(` ${green}✓${r} Claude Code MCP URL reverted to /mcp (0.6.14 /mcp/bearer was wrong for CC)`);
1800
- }
1801
- if (_mcpFix.deduped) {
1802
- console.log(` ${green}✓${r} Removed stale local-scope patchcord MCP (use project .mcp.json only)`);
1803
- }
1804
-
1805
1722
  // ── Purge stale npx cache entries ────────────────────────────
1806
1723
  // npx never auto-updates a cached package. Users with an old `npx patchcord`
1807
1724
  // entry get the stale binary indefinitely. On every install we sweep
@@ -1867,11 +1784,9 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1867
1784
  let globalChanges = [];
1868
1785
  if (globalCliInstalled) globalChanges.push("Patchcord CLI installed globally");
1869
1786
 
1870
- // Claude Code — skip when install runs inside cursor-agent: Claude's plugin
1871
- // MCP registers as "plugin-patchcord-marketplace-patchcord", which cursor-agent
1872
- // tries instead of project .cursor/mcp.json → "MCP server not found".
1787
+ // Claude Code
1873
1788
  const hasClaude = run("which claude");
1874
- if (hasClaude && !skipClaudePluginForCursor) {
1789
+ if (hasClaude) {
1875
1790
  // Remove legacy npm-cache install (pre-marketplace era) — causes duplicate /patchcord commands.
1876
1791
  const npmCachePatchcord = join(HOME, ".claude", "plugins", "npm-cache", "node_modules", "patchcord");
1877
1792
  if (existsSync(npmCachePatchcord)) {
@@ -1985,30 +1900,6 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1985
1900
  } else if (allowlistChanged) {
1986
1901
  globalChanges.push("Cursor MCP allowlist configured");
1987
1902
  }
1988
-
1989
- // cursor-agent v2026.07+ also reads ~/.cursor/cli-config.json permissions.allow
1990
- // for MCP tool approval — permissions.json alone leaves patchcord at
1991
- // "not loaded (needs approval)" even with --yolo.
1992
- const cliConfigPath = join(HOME, ".cursor", "cli-config.json");
1993
- let cliAllowChanged = false;
1994
- const cliOk = updateJsonConfig(cliConfigPath, (obj) => {
1995
- obj.permissions = obj.permissions || {};
1996
- if (!obj.permissions.allow) obj.permissions.allow = [];
1997
- if (!Array.isArray(obj.permissions.allow)) {
1998
- console.log(`\n ${yellow}⚠${r} Skipped Cursor CLI MCP allow — ${cliConfigPath} permissions.allow is not an array.`);
1999
- return;
2000
- }
2001
- const pat = "Mcp(patchcord:*)";
2002
- if (!obj.permissions.allow.includes(pat)) {
2003
- obj.permissions.allow.push(pat);
2004
- cliAllowChanged = true;
2005
- }
2006
- });
2007
- if (!cliOk) {
2008
- globalChanges.push("✗ Cursor CLI permissions error");
2009
- } else if (cliAllowChanged) {
2010
- globalChanges.push("Cursor CLI MCP allowlist configured");
2011
- }
2012
1903
  }
2013
1904
  }
2014
1905
 
@@ -2446,13 +2337,6 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
2446
2337
  }
2447
2338
  toolSlug = toolFlag; // preserved as-is for the URL param
2448
2339
  }
2449
- // cursor-agent --yolo runs `npx patchcord` without --tool=cursor. Without
2450
- // this, browser connect defaults to Claude and writes .mcp.json instead of
2451
- // .cursor/mcp.json — MCP never loads in cursor-agent.
2452
- if (!choice && process.env.CURSOR_AGENT) {
2453
- choice = CLIENT_TYPE_MAP.cursor;
2454
- toolSlug = toolSlug || "cursor";
2455
- }
2456
2340
 
2457
2341
  // --token bypass for power users / CI / self-hosters
2458
2342
  const tokenFlag = flags.find(f => f.startsWith("--token="))?.split("=")[1]
@@ -2503,14 +2387,12 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
2503
2387
  let existingToken = "";
2504
2388
  let existingConfigFile = "";
2505
2389
  const mcpJsonPath = join(cwd, ".mcp.json");
2506
- const cursorMcpPath = join(cwd, ".cursor", "mcp.json");
2507
2390
  const codexTomlPath = join(cwd, ".codex", "config.toml");
2508
2391
  const grokTomlPath = join(cwd, ".grok", "config.toml");
2509
2392
  const kimiJsonPath = join(cwd, ".kimi", "mcp.json");
2510
2393
 
2511
- const slugForCheck = toolSlug ? toolSlug.replace(/-/g, "_") : (process.env.CURSOR_AGENT ? "cursor" : "");
2394
+ const slugForCheck = toolSlug ? toolSlug.replace(/-/g, "_") : "";
2512
2395
  const checkMcpJson = !slugForCheck || slugForCheck === "claude_code";
2513
- const checkCursorJson = !slugForCheck || slugForCheck === "cursor";
2514
2396
  const checkCodexToml = !slugForCheck || slugForCheck === "codex";
2515
2397
  const checkGrokToml = !slugForCheck || ["grok", "grok_cli", "grok_build"].includes(slugForCheck);
2516
2398
  const checkKimiJson = !slugForCheck || slugForCheck === "kimi";
@@ -2525,16 +2407,6 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
2525
2407
  }
2526
2408
  } catch {}
2527
2409
  }
2528
- if (!existingToken && checkCursorJson && existsSync(cursorMcpPath)) {
2529
- try {
2530
- const existing = JSON.parse(readFileSync(cursorMcpPath, "utf-8"));
2531
- const pt = existing?.mcpServers?.patchcord;
2532
- if (pt?.headers?.Authorization) {
2533
- existingToken = pt.headers.Authorization.replace(/^Bearer\s+/i, "");
2534
- existingConfigFile = cursorMcpPath;
2535
- }
2536
- } catch {}
2537
- }
2538
2410
  if (!existingToken && checkCodexToml && existsSync(codexTomlPath)) {
2539
2411
  try {
2540
2412
  const content = readFileSync(codexTomlPath, "utf-8");
@@ -2857,18 +2729,6 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
2857
2729
  console.log(`\n ${green}✓${r} Cursor configured: ${dim}${cursorPath}${r}`);
2858
2730
  console.log(` ${dim}Per-project only — other projects won't see this agent.${r}`);
2859
2731
  }
2860
- // Project-layer CLI permissions (cursor-agent merges .cursor/cli.json from git root).
2861
- const cursorCliPath = join(cursorDir, "cli.json");
2862
- const cursorCliOk = updateJsonConfig(cursorCliPath, (obj) => {
2863
- obj.permissions = obj.permissions || {};
2864
- if (!obj.permissions.allow) obj.permissions.allow = [];
2865
- if (!Array.isArray(obj.permissions.allow)) return;
2866
- const pat = "Mcp(patchcord:*)";
2867
- if (!obj.permissions.allow.includes(pat)) obj.permissions.allow.push(pat);
2868
- });
2869
- if (cursorCliOk) {
2870
- console.log(` ${green}✓${r} Cursor CLI permissions: ${dim}${cursorCliPath}${r}`);
2871
- }
2872
2732
  } else if (isGrok) {
2873
2733
  // Grok CLI: project-scoped .grok/config.toml. Grok loads this file from
2874
2734
  // the current directory up through the git root, so the bearer remains
@@ -3410,7 +3270,6 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
3410
3270
  // their JSON and re-runs.
3411
3271
  process.exit(1);
3412
3272
  }
3413
- dedupeClaudePatchcordMcp(cwd);
3414
3273
  console.log(`\n ${green}✓${r} Claude Code configured: ${dim}${mcpPath}${r}`);
3415
3274
 
3416
3275
  // Statusline: ask whether to enable full mode (unless --full flag passed or already configured).
@@ -3526,9 +3385,6 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
3526
3385
  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}`);
3527
3386
  } else if (isKimi) {
3528
3387
  console.log(`\n ${green}→${r} ${bold}Restart your ${toolName} session with ${cyan}kimi-pc${r}${bold}, then say: ${cyan}${bold}check inbox${r}`);
3529
- } else if (isCursor && hasCursorAgent) {
3530
- 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}`);
3531
- console.log(` ${dim}Then:${r} ${cyan}${bold}check inbox${r} ${dim}before${r} ${cyan}${bold}/patchcord-subscribe${r}`);
3532
3388
  } else {
3533
3389
  console.log(`\n ${green}→${r} ${bold}Restart your ${toolName} session${r}, then say: ${cyan}${bold}check inbox${r}`);
3534
3390
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.6.19",
3
+ "version": "0.6.21",
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"