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
|
-
//
|
|
563
|
-
|
|
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_type — default 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
|
@@ -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
|
|
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
|
|
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
|
|
37
|
-
- If it's clearly for another chat session
|
|
38
|
-
- If there's no tag
|
|
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
|
|
package/skills/inbox/SKILL.md
CHANGED
|
@@ -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
|
|
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.
|