patchcord 0.3.88 → 0.3.90

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/bin/patchcord.mjs CHANGED
@@ -263,7 +263,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
263
263
 
264
264
  const CLIENT_TYPE_MAP = {
265
265
  "claude_code": "1", "codex": "2", "cursor": "3", "windsurf": "4",
266
- "gemini": "5", "vscode": "6", "zed": "7", "opencode": "8", "openclaw": "9",
266
+ "gemini": "5", "vscode": "6", "zed": "7", "opencode": "8", "openclaw": "9", "antigravity": "10",
267
267
  };
268
268
 
269
269
 
@@ -365,9 +365,23 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
365
365
  } catch {}
366
366
  }
367
367
  }
368
+ if (!existingToken) {
369
+ const agJsonPath = join(HOME, ".gemini", "antigravity", "mcp_config.json");
370
+ if (existsSync(agJsonPath)) {
371
+ try {
372
+ const ag = JSON.parse(readFileSync(agJsonPath, "utf-8"));
373
+ const pt = ag?.mcpServers?.patchcord;
374
+ if (pt?.headers?.Authorization) {
375
+ existingToken = pt.headers.Authorization.replace(/^Bearer\s+/i, "");
376
+ existingConfigFile = agJsonPath;
377
+ }
378
+ } catch {}
379
+ }
380
+ }
368
381
  if (existingToken) {
369
382
  // Figure out which tool is already configured
370
383
  const existingToolName = existingConfigFile.includes(".codex") ? "Codex"
384
+ : existingConfigFile.includes("antigravity") ? "Antigravity"
371
385
  : existingConfigFile.includes("openclaw") ? "OpenClaw"
372
386
  : existingConfigFile.includes(".cursor") ? "Cursor"
373
387
  : existingConfigFile.includes(".vscode") ? "VS Code"
@@ -559,22 +573,8 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
559
573
  console.log(` ${green}✓${r} ${bold}${identity}${r} connected.`);
560
574
 
561
575
  if (!choice) {
562
- // Backend didn't send tool type ask in terminal
563
- const { createInterface: createRL3 } = await import("readline");
564
- const rl3 = createRL3({ input: process.stdin, output: process.stdout });
565
- const ask3 = (q) => new Promise((resolve) => rl3.question(q, resolve));
566
- console.log(`\n${bold}Which tool are you setting up?${r}\n`);
567
- console.log(` ${cyan}1.${r} Claude Code ${cyan}5.${r} Gemini CLI`);
568
- console.log(` ${cyan}2.${r} Codex CLI ${cyan}6.${r} VS Code`);
569
- console.log(` ${cyan}3.${r} Cursor ${cyan}7.${r} Zed`);
570
- console.log(` ${cyan}4.${r} Windsurf ${cyan}8.${r} OpenCode`);
571
- console.log(` ${cyan}9.${r} OpenClaw\n`);
572
- choice = (await ask3(`${dim}Choose (1-9):${r} `)).trim();
573
- rl3.close();
574
- if (!["1","2","3","4","5","6","7","8","9"].includes(choice)) {
575
- console.error("Invalid choice.");
576
- process.exit(1);
577
- }
576
+ // Web UI didn't send client_typedefault to Claude Code
577
+ choice = "1";
578
578
  }
579
579
  }
580
580
  } // end connect flow
@@ -588,6 +588,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
588
588
  const isZed = choice === "7";
589
589
  const isOpenCode = choice === "8";
590
590
  const isOpenClaw = choice === "9";
591
+ const isAntigravity = choice === "10";
591
592
 
592
593
  const hostname = run("hostname -s") || run("hostname") || "unknown";
593
594
 
@@ -761,6 +762,28 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
761
762
  console.log(`\n ${dim}If tools don't appear after restart, your OpenClaw may be too old${r}`);
762
763
  console.log(` ${dim}for streamable-http. Update to v2026.3.31+ or use mcp-remote:${r}`);
763
764
  console.log(` ${dim}openclaw mcp set patchcord '{"command":"npx","args":["mcp-remote","${serverUrl}/mcp","--header","Authorization: Bearer ${token}"],"transport":"stdio"}'${r}`);
765
+ } else if (isAntigravity) {
766
+ // Antigravity: global ~/.gemini/antigravity/mcp_config.json → mcpServers
767
+ const agDir = join(HOME, ".gemini", "antigravity");
768
+ const agPath = join(agDir, "mcp_config.json");
769
+ let agConfig = {};
770
+ if (existsSync(agPath)) {
771
+ try {
772
+ agConfig = JSON.parse(readFileSync(agPath, "utf-8"));
773
+ } catch {}
774
+ }
775
+ if (!agConfig.mcpServers) agConfig.mcpServers = {};
776
+ agConfig.mcpServers.patchcord = {
777
+ serverUrl: `${serverUrl}/mcp`,
778
+ headers: {
779
+ Authorization: `Bearer ${token}`,
780
+ "X-Patchcord-Machine": hostname,
781
+ },
782
+ };
783
+ mkdirSync(agDir, { recursive: true });
784
+ writeFileSync(agPath, JSON.stringify(agConfig, null, 2) + "\n");
785
+ console.log(`\n ${green}✓${r} Antigravity configured: ${dim}${agPath}${r}`);
786
+ console.log(` ${yellow}Global config — all Antigravity projects share this agent.${r}`);
764
787
  } else if (isVSCode) {
765
788
  // VS Code: write .vscode/mcp.json (per-project)
766
789
  const vscodeDir = join(cwd, ".vscode");
@@ -936,7 +959,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
936
959
  }
937
960
 
938
961
  // Warn about gitignore for per-project configs with tokens
939
- if (!isWindsurf && !isGemini && !isZed && !isOpenClaw) {
962
+ if (!isWindsurf && !isGemini && !isZed && !isOpenClaw && !isAntigravity) {
940
963
  const gitignorePath = join(cwd, ".gitignore");
941
964
  const configFile = isCodex ? ".codex/config.toml" : isCursor ? ".cursor/mcp.json" : isVSCode ? ".vscode/mcp.json" : isOpenCode ? "opencode.json" : ".mcp.json";
942
965
  let needsWarning = true;
@@ -951,9 +974,9 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
951
974
  }
952
975
  }
953
976
 
954
- const toolName = isOpenClaw ? "OpenClaw" : isOpenCode ? "OpenCode" : isZed ? "Zed" : isVSCode ? "VS Code" : isGemini ? "Gemini CLI" : isWindsurf ? "Windsurf" : isCursor ? "Cursor" : isCodex ? "Codex" : "Claude Code";
977
+ const toolName = isAntigravity ? "Antigravity" : isOpenClaw ? "OpenClaw" : isOpenCode ? "OpenCode" : isZed ? "Zed" : isVSCode ? "VS Code" : isGemini ? "Gemini CLI" : isWindsurf ? "Windsurf" : isCursor ? "Cursor" : isCodex ? "Codex" : "Claude Code";
955
978
 
956
- if (!isWindsurf && !isGemini && !isZed && !isOpenClaw) {
979
+ if (!isWindsurf && !isGemini && !isZed && !isOpenClaw && !isAntigravity) {
957
980
  console.log(`\n ${dim}To connect a second agent:${r}`);
958
981
  console.log(` ${dim}cd into another project and run${r} ${bold}npx patchcord@latest${r} ${dim}there.${r}`);
959
982
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.3.88",
3
+ "version": "0.3.90",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "author": "ppravdin",
6
6
  "license": "MIT",
@@ -17,7 +17,7 @@ project's MCP config.
17
17
 
18
18
  - `inbox(all_agents?)` - read pending messages, current identity, and recently active agents. `all_agents=true` includes inactive agents. Presence tells you whether to wait for a reply after sending, not whether to send.
19
19
  - `send_message(to_agent, content)` - send a message. Comma-separated for multiple: `send_message("backend, frontend", "hello")`. Use `@username` for cross-user Gate messaging. Messages support up to 50,000 characters - send full content, specs, and code as-is. Never summarize or truncate.
20
- - `reply(message_id, content, defer?, resolve?)` - reply to a received message. `defer=true` keeps the original visible in inbox for later (survives context compaction). `resolve=true` signals thread complete, notifies sender no reply needed.
20
+ - `reply(message_id, content?, defer?, resolve?)` - reply to a received message. `defer=true` keeps the original visible in inbox for later (survives context compaction). `resolve=true` marks the message as handled content is optional, use `reply(message_id, resolve=true)` to silently close a thread without sending anything.
21
21
  - `wait_for_message(timeout_seconds?)` - block until incoming message arrives. Default 5 minutes. Known to error intermittently - if it fails, poll inbox() every 10-15 seconds as fallback.
22
22
  - `attachment(...)` - upload, download, or relay files between agents (see File sharing below)
23
23
  - `recall(limit?, from_agent?)` - view recent message history including already-read messages. `from_agent` filters by sender. For debugging only, not routine use.
@@ -98,7 +98,7 @@ Then send the `path` to the other agent. No base64, no token waste.
98
98
  ```
99
99
  attachment(upload=true, filename="notes.txt", file_data="<base64>")
100
100
  ```
101
- Never use for files on disk — use presigned upload above instead.
101
+ Base64 adds ~33% overhead and wastes context tokens. Never use this for files on disk — use presigned upload above instead.
102
102
 
103
103
  **Downloading:**
104
104
  ```
@@ -13,7 +13,7 @@ You are connected to Patchcord, a message bus that lets you talk to AI agents on
13
13
 
14
14
  - **inbox(all_agents?)** - read pending messages + recent activity. `all_agents=true` includes inactive agents. Presence tells you whether to wait for a reply, not whether to send. Online = set up wait_for_message after sending. Offline = send and move on, don't wait.
15
15
  - **send_message(to_agent, content)** - send a message. Comma-separated for multiple: `send_message("backend, frontend", "hello")`. Supports `@username` for cross-user Gate messaging. Up to 50,000 characters - send full content, never summarize.
16
- - **reply(message_id, content, defer?, resolve?)** - reply to a received message. `defer=true` keeps message visible in inbox (survives context compaction). `resolve=true` signals conversation complete, notifies sender no reply needed.
16
+ - **reply(message_id, content?, defer?, resolve?)** - reply to a received message. `defer=true` keeps message visible in inbox (survives context compaction). `resolve=true` marks as handled content is optional, use `reply(message_id, resolve=true)` to silently close a thread.
17
17
  - **wait_for_message(timeout_seconds?)** - block until incoming message arrives. Default 300s. Known to error intermittently - if it fails, poll inbox() in a loop as fallback.
18
18
  - **attachment(...)** - file operations (see File sharing section below)
19
19
  - **recall(limit?, from_agent?)** - view recent message history including already-read messages. Debugging only, not routine use. `from_agent` filters by sender.
@@ -33,9 +33,11 @@ You may be one of several chat sessions sharing the same Patchcord identity. To
33
33
  Use the dominant topic of your current conversation as the tag. Keep it short (1-3 words). Be consistent within a session - pick a tag early and reuse it.
34
34
 
35
35
  **When receiving messages**, check the context tag:
36
- - If it matches your chat's topic - reply normally
37
- - If it's clearly for another chat session - reply with: "This seems intended for the [tag] chat. Leaving unread for them." Then use `reply(message_id, "Routed to [tag] chat", defer=true)` so the message stays visible for the right session.
38
- - If there's no tag or it's ambiguous - handle it normally
36
+ - If it matches your chat's topic reply normally
37
+ - If it's clearly for another chat session `reply(message_id, " [tag] chat", defer=true)`. Minimal content, no explanation.
38
+ - If there's no tag but the content is addressed to a different role (e.g. "To: claudeai (UI/UX designer)" when you're the scientific supervisor) — treat as wrong-chat. `reply(message_id, "→ other session", defer=true)`. Do not explain your role or what the other session should do.
39
+ - If there's no tag and it's ambiguous — handle it normally
40
+ - When a message has no context tag but is addressed "To: [role]" in the body, the role-line acts as a tag. Route accordingly.
39
41
 
40
42
  ## Behavioral rules
41
43
 
@@ -51,7 +53,7 @@ Use the dominant topic of your current conversation as the tag. Keep it short (1
51
53
 
52
54
  6. **Never show raw JSON** - summarize naturally.
53
55
 
54
- 7. **Do not reply to acks**: "ok", "noted", "seen", "thanks", thumbs up, or conversation-ending signals. Only reply when a question is asked, an action is requested, or a deliverable is expected. Use `resolve=true` on your reply when a thread is done.
56
+ 7. **Do not reply to acks**: "ok", "noted", "seen", "thanks", thumbs up, or conversation-ending signals. Only reply when a question is asked, an action is requested, or a deliverable is expected. Use `resolve=true` on your reply when a thread is done. This applies even when an ack is for another session — don't defer-route acks, just leave them alone.
55
57
 
56
58
  8. **Presence is not a delivery gate**: an agent may receive messages while absent from the online list. Always send regardless of online/offline status. Messages queue and deliver when the recipient checks inbox.
57
59
 
@@ -54,7 +54,8 @@ If send_message fails with a send gate error: call inbox(), reply to or resolve
54
54
  2. Do the work described in the message - using your project's actual code, real files, real lines
55
55
  3. Reply with what you did, choosing the right flag:
56
56
  - `reply(message_id, "done: [details]")` — work done, sender might follow up
57
- - `reply(message_id, "done: [details]", resolve=true)` — work done, conversation finished. Notifies the sender.
57
+ - `reply(message_id, "done: [details]", resolve=true)` — work done, conversation finished
58
+ - `reply(message_id, resolve=true)` — silently close a thread without sending anything (e.g. clearing misfired messages)
58
59
  - `reply(message_id, "ack, prioritizing [other task] first", defer=true)` — you acknowledged but haven't done the work yet. The message stays in your inbox as a reminder.
59
60
  4. wait_for_message() if the sender is online - stay responsive for follow-ups
60
61
  5. If you can't do the work, say specifically what's blocking you. Don't guess about another agent's code.