patchcord 0.3.6 → 0.3.7
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 +77 -102
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "patchcord",
|
|
3
3
|
"description": "Cross-machine agent messaging with auto-inbox checking. Agents automatically respond to messages from other agents without human intervention.",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.7",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "ppravdin"
|
|
7
7
|
},
|
package/bin/patchcord.mjs
CHANGED
|
@@ -21,12 +21,10 @@ if (!cmd || cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
|
21
21
|
console.log(`patchcord — agent messaging for Claude Code & Codex
|
|
22
22
|
|
|
23
23
|
Commands:
|
|
24
|
-
patchcord install
|
|
25
|
-
patchcord install --full
|
|
26
|
-
patchcord agent Set up MCP config for
|
|
27
|
-
patchcord
|
|
28
|
-
patchcord skill apply Fetch and apply custom skill from the web console
|
|
29
|
-
patchcord skill reinstall Full rewrite: default skill + custom skill from server
|
|
24
|
+
patchcord install One-time global setup (auto-detects Claude Code + Codex)
|
|
25
|
+
patchcord install --full Same + enable full statusline (model, context%, git)
|
|
26
|
+
patchcord agent Set up MCP config for this project (auto-detects tool)
|
|
27
|
+
patchcord skill apply Fetch custom skill from web console
|
|
30
28
|
|
|
31
29
|
Run "patchcord install" once. Run "patchcord agent" in each project.`);
|
|
32
30
|
process.exit(0);
|
|
@@ -37,101 +35,107 @@ if (cmd === "plugin-path") {
|
|
|
37
35
|
process.exit(0);
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
// ── install: global
|
|
38
|
+
// ── install: global setup for all detected tools (idempotent) ──
|
|
41
39
|
if (cmd === "install") {
|
|
42
|
-
const hasClaude = run("which claude");
|
|
43
|
-
if (!hasClaude) {
|
|
44
|
-
console.log(`Claude Code CLI not found. Install it first:
|
|
45
|
-
https://claude.ai/code
|
|
46
|
-
|
|
47
|
-
Then run: patchcord install`);
|
|
48
|
-
process.exit(1);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
40
|
const flags = process.argv.slice(3);
|
|
52
41
|
const fullStatusline = flags.includes("--full");
|
|
42
|
+
const { readFileSync, writeFileSync } = await import("fs");
|
|
53
43
|
|
|
54
|
-
|
|
44
|
+
let installedSomething = false;
|
|
55
45
|
|
|
56
|
-
//
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
46
|
+
// ── Claude Code ──
|
|
47
|
+
const hasClaude = run("which claude");
|
|
48
|
+
if (hasClaude) {
|
|
49
|
+
console.log("Found Claude Code. Installing patchcord plugin...");
|
|
50
|
+
|
|
51
|
+
// Register npm package as a local marketplace (idempotent)
|
|
52
|
+
const marketplaceExists = run(`claude plugin marketplace list`)?.includes("patchcord");
|
|
53
|
+
if (!marketplaceExists) {
|
|
54
|
+
run(`claude plugin marketplace add "${pluginRoot}"`);
|
|
65
55
|
}
|
|
66
|
-
}
|
|
67
56
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
57
|
+
// Install or update the plugin from the marketplace
|
|
58
|
+
const installed = run(`claude plugin list`)?.includes("patchcord");
|
|
59
|
+
installed ? run(`claude plugin update patchcord`) : run(`claude plugin install patchcord`);
|
|
60
|
+
|
|
61
|
+
// Block OAuth tool leakage from claude.ai web connector
|
|
62
|
+
const claudeSettings = join(process.env.HOME || "", ".claude", "settings.json");
|
|
63
|
+
if (existsSync(claudeSettings)) {
|
|
64
|
+
try {
|
|
65
|
+
const settings = JSON.parse(readFileSync(claudeSettings, "utf-8"));
|
|
66
|
+
const deny = settings.permissions?.deny || [];
|
|
67
|
+
if (!deny.includes("mcp__claude_ai_Patchcord__*")) {
|
|
68
|
+
if (!settings.permissions) settings.permissions = {};
|
|
69
|
+
if (!settings.permissions.deny) settings.permissions.deny = [];
|
|
70
|
+
settings.permissions.deny.push("mcp__claude_ai_Patchcord__*");
|
|
71
|
+
writeFileSync(claudeSettings, JSON.stringify(settings, null, 2) + "\n");
|
|
72
|
+
console.log("✓ Blocked OAuth tool leakage from claude.ai web connector.");
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
// Non-fatal — settings.json might be malformed
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Enable statusline
|
|
80
|
+
const enableScript = join(pluginRoot, "scripts", "enable-statusline.sh");
|
|
81
|
+
if (existsSync(enableScript)) {
|
|
82
|
+
const slArg = fullStatusline ? " --full" : "";
|
|
83
|
+
run(`bash "${enableScript}"${slArg}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log(`✓ Claude Code plugin installed.${fullStatusline ? " Full statusline enabled." : ""}`);
|
|
87
|
+
installedSomething = true;
|
|
78
88
|
}
|
|
79
89
|
|
|
80
|
-
//
|
|
90
|
+
// ── Codex CLI ──
|
|
81
91
|
const codexConfig = join(process.env.HOME || "", ".codex", "config.toml");
|
|
82
92
|
if (existsSync(codexConfig)) {
|
|
83
|
-
|
|
93
|
+
console.log("Found Codex CLI config.");
|
|
94
|
+
|
|
95
|
+
// Disable patchcord as ChatGPT app (prevents OAuth identity conflict)
|
|
84
96
|
const content = readFileSync(codexConfig, "utf-8");
|
|
85
97
|
if (!content.includes("[apps.patchcord]")) {
|
|
86
98
|
writeFileSync(codexConfig, content.trimEnd() + "\n\n[apps.patchcord]\nenabled = false\n");
|
|
87
99
|
console.log("✓ Disabled patchcord ChatGPT app in Codex (prevents identity conflict).");
|
|
88
100
|
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Enable statusline
|
|
92
|
-
const enableScript = join(pluginRoot, "scripts", "enable-statusline.sh");
|
|
93
|
-
if (existsSync(enableScript)) {
|
|
94
|
-
const slArg = fullStatusline ? " --full" : "";
|
|
95
|
-
const slResult = run(`bash "${enableScript}"${slArg}`);
|
|
96
|
-
if (slResult !== null) {
|
|
97
|
-
console.log(`✓ Plugin installed. Statusline${fullStatusline ? " (full)" : ""} enabled.
|
|
98
101
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
: "Statusline shows: agent@namespace │ inbox\n Tip: run \"patchcord install --full\" for model, context%, git info too."}
|
|
102
|
+
installedSomething = true;
|
|
103
|
+
}
|
|
102
104
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
console.log(`✓ Plugin installed. Statusline setup skipped (non-fatal).
|
|
105
|
+
if (!installedSomething) {
|
|
106
|
+
console.log(`No Claude Code or Codex CLI detected.
|
|
106
107
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
console.log(`✓ Plugin installed.
|
|
108
|
+
Install one first:
|
|
109
|
+
Claude Code: https://claude.ai/code
|
|
110
|
+
Codex CLI: npm install -g @openai/codex
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
Then run: npx patchcord@latest install`);
|
|
113
|
+
process.exit(1);
|
|
113
114
|
}
|
|
115
|
+
|
|
116
|
+
console.log(`\nRun "npx patchcord@latest agent" in each project to set up MCP.`);
|
|
114
117
|
process.exit(0);
|
|
115
118
|
}
|
|
116
119
|
|
|
117
|
-
// ── agent: per-project MCP setup
|
|
120
|
+
// ── agent: per-project MCP setup (auto-detects tool) ──────────
|
|
118
121
|
if (cmd === "agent") {
|
|
119
|
-
const flag = process.argv[3];
|
|
120
122
|
const cwd = process.cwd();
|
|
121
123
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
// Auto-detect: Codex project has .agents folder or .codex folder
|
|
125
|
+
const isCodex = existsSync(join(cwd, ".agents")) || existsSync(join(cwd, ".codex"));
|
|
126
|
+
|
|
127
|
+
if (isCodex) {
|
|
128
|
+
// Codex: copy skill
|
|
124
129
|
const dest = join(cwd, ".agents", "skills", "patchcord");
|
|
125
130
|
mkdirSync(dest, { recursive: true });
|
|
126
131
|
cpSync(join(pluginRoot, "codex", "SKILL.md"), join(dest, "SKILL.md"));
|
|
127
132
|
console.log(`✓ Codex skill installed: ${dest}/SKILL.md
|
|
128
133
|
|
|
129
|
-
Add to
|
|
134
|
+
Add to .codex/config.toml in this project:
|
|
130
135
|
|
|
131
136
|
[mcp_servers.patchcord]
|
|
132
|
-
url = "https://YOUR_SERVER/mcp"
|
|
133
|
-
|
|
134
|
-
http_headers = { "X-Patchcord-Client-Type" = "codex" }`);
|
|
137
|
+
url = "https://YOUR_SERVER/mcp/bearer"
|
|
138
|
+
http_headers = { "Authorization" = "Bearer YOUR_TOKEN", "X-Patchcord-Client-Type" = "codex" }`);
|
|
135
139
|
} else {
|
|
136
140
|
// Claude Code: print .mcp.json template
|
|
137
141
|
console.log(`Add .mcp.json to this project:
|
|
@@ -142,8 +146,7 @@ Add to ~/.codex/config.toml:
|
|
|
142
146
|
"type": "http",
|
|
143
147
|
"url": "https://YOUR_SERVER/mcp",
|
|
144
148
|
"headers": {
|
|
145
|
-
"Authorization": "Bearer YOUR_TOKEN"
|
|
146
|
-
"X-Patchcord-Client-Type": "claude_code"
|
|
149
|
+
"Authorization": "Bearer YOUR_TOKEN"
|
|
147
150
|
}
|
|
148
151
|
}
|
|
149
152
|
}
|
|
@@ -153,8 +156,7 @@ Or use the CLI:
|
|
|
153
156
|
|
|
154
157
|
claude mcp add patchcord "https://YOUR_SERVER/mcp" \\
|
|
155
158
|
--transport http -s project \\
|
|
156
|
-
-H "Authorization: Bearer YOUR_TOKEN"
|
|
157
|
-
-H "X-Patchcord-Client-Type: claude_code"`);
|
|
159
|
+
-H "Authorization: Bearer YOUR_TOKEN"`);
|
|
158
160
|
}
|
|
159
161
|
process.exit(0);
|
|
160
162
|
}
|
|
@@ -163,12 +165,12 @@ Or use the CLI:
|
|
|
163
165
|
if (cmd === "init") {
|
|
164
166
|
console.log(`"patchcord init" is now two commands:
|
|
165
167
|
|
|
166
|
-
patchcord install
|
|
168
|
+
patchcord install One-time global setup (once)
|
|
167
169
|
patchcord agent Set up MCP for this project (per project)`);
|
|
168
170
|
process.exit(0);
|
|
169
171
|
}
|
|
170
172
|
|
|
171
|
-
// ── skill: custom skill
|
|
173
|
+
// ── skill: custom skill from web console ─────────────────────
|
|
172
174
|
if (cmd === "skill") {
|
|
173
175
|
const sub = process.argv[3];
|
|
174
176
|
const cwd = process.cwd();
|
|
@@ -215,9 +217,6 @@ if (cmd === "skill") {
|
|
|
215
217
|
process.exit(1);
|
|
216
218
|
}
|
|
217
219
|
|
|
218
|
-
// Custom skill goes to .claude/skills/patchcord-custom/SKILL.md
|
|
219
|
-
// Claude Code auto-discovers project-level skills from this directory.
|
|
220
|
-
// Only the custom part — default patchcord skill is already loaded globally by the plugin.
|
|
221
220
|
const skillDir = join(cwd, ".claude", "skills", "patchcord-custom");
|
|
222
221
|
const skillFile = join(skillDir, "SKILL.md");
|
|
223
222
|
|
|
@@ -241,34 +240,10 @@ if (cmd === "skill") {
|
|
|
241
240
|
console.error("Failed to parse skill response.");
|
|
242
241
|
process.exit(1);
|
|
243
242
|
}
|
|
244
|
-
} else if (sub === "reinstall") {
|
|
245
|
-
console.log(`Fetching custom skill for ${namespace}:${agentId}...`);
|
|
246
|
-
const resp = run(`curl -s -H "Authorization: Bearer ${token}" "${baseUrl}/api/skills/${namespace}/${agentId}"`);
|
|
247
|
-
try {
|
|
248
|
-
const data = JSON.parse(resp || "{}");
|
|
249
|
-
if (data.skill_text) {
|
|
250
|
-
mkdirSync(skillDir, { recursive: true });
|
|
251
|
-
writeFileSync(skillFile, data.skill_text.trim() + "\n");
|
|
252
|
-
console.log(`✓ Custom skill applied to ${skillFile}`);
|
|
253
|
-
} else {
|
|
254
|
-
// Remove custom skill if none set
|
|
255
|
-
if (existsSync(skillFile)) {
|
|
256
|
-
const { unlinkSync } = await import("fs");
|
|
257
|
-
unlinkSync(skillFile);
|
|
258
|
-
console.log("Custom skill removed (none set on server).");
|
|
259
|
-
} else {
|
|
260
|
-
console.log("No custom skill set for this agent.");
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
} catch {
|
|
264
|
-
console.log("No custom skill set or server unreachable.");
|
|
265
|
-
}
|
|
266
243
|
} else {
|
|
267
|
-
console.log(`
|
|
268
|
-
Usage:
|
|
269
|
-
patchcord skill apply Fetch and apply custom skill from server
|
|
270
|
-
patchcord skill reinstall Re-fetch custom skill from server`);
|
|
244
|
+
console.log(`Usage: patchcord skill apply`);
|
|
271
245
|
}
|
|
246
|
+
|
|
272
247
|
// Clean up old PATCHCORD.md if it exists
|
|
273
248
|
const oldFile = join(cwd, "PATCHCORD.md");
|
|
274
249
|
if (existsSync(oldFile)) {
|