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.
- package/.claude-plugin/plugin.json +1 -1
- package/bin/patchcord.mjs +31 -103
- package/package.json +1 -1
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
|
|
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+
|
|
1967
|
-
// for MCP
|
|
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
|
|
2427
|
-
//
|
|
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