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.
@@ -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.6",
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 Install/update plugin globally (Claude Code)
25
- patchcord install --full Install + enable full statusline (model, context%, git)
26
- patchcord agent Set up MCP config for an agent in this project
27
- patchcord agent --codex Set up Codex skill + MCP config in this project
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 plugin + skills (idempotent) ──────────────
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
- console.log("Installing patchcord plugin into Claude Code...");
44
+ let installedSomething = false;
55
45
 
56
- // Register npm package as a local marketplace (idempotent)
57
- const marketplaceExists = run(`claude plugin marketplace list`)?.includes("patchcord");
58
- if (!marketplaceExists) {
59
- const addResult = run(`claude plugin marketplace add "${pluginRoot}"`);
60
- if (addResult === null) {
61
- console.log(`✗ Could not add marketplace. Try manually:
62
- claude plugin marketplace add "${pluginRoot}"
63
- claude plugin install patchcord`);
64
- process.exit(1);
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
- // Install or update the plugin from the marketplace
69
- const installed = run(`claude plugin list`)?.includes("patchcord");
70
- const result = installed
71
- ? run(`claude plugin update patchcord`)
72
- : run(`claude plugin install patchcord`);
73
- if (result === null && !installed) {
74
- console.log(`✗ Plugin install failed. Try manually:
75
- claude plugin marketplace add "${pluginRoot}"
76
- claude plugin install patchcord`);
77
- process.exit(1);
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
- // Disable patchcord as Codex ChatGPT app (prevents OAuth identity conflict)
90
+ // ── Codex CLI ──
81
91
  const codexConfig = join(process.env.HOME || "", ".codex", "config.toml");
82
92
  if (existsSync(codexConfig)) {
83
- const { readFileSync, writeFileSync } = await import("fs");
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
- ${fullStatusline
100
- ? "Statusline shows: model │ context% │ repo (branch) │ agent@namespace │ inbox"
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
- Run "patchcord agent" in each project to set up MCP.`);
104
- } else {
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
- Run "patchcord agent" in each project to set up MCP.`);
108
- }
109
- } else {
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
- Run "patchcord agent" in each project to set up MCP.`);
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
- if (flag === "--codex" || (!flag && existsSync(join(cwd, ".agents")))) {
123
- // Codex: copy skill + print MCP config
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 ~/.codex/config.toml:
134
+ Add to .codex/config.toml in this project:
130
135
 
131
136
  [mcp_servers.patchcord]
132
- url = "https://YOUR_SERVER/mcp"
133
- bearer_token_env_var = "PATCHCORD_TOKEN"
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 Install/update plugin globally (once)
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 management ───────────────────────────
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(`Unknown skill subcommand: ${sub}
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)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "author": "ppravdin",
6
6
  "license": "MIT",