patchcord 0.3.87 → 0.3.89

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
@@ -559,22 +559,8 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
559
559
  console.log(` ${green}✓${r} ${bold}${identity}${r} connected.`);
560
560
 
561
561
  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
- }
562
+ // Web UI didn't send client_typedefault to Claude Code
563
+ choice = "1";
578
564
  }
579
565
  }
580
566
  } // end connect flow
@@ -799,7 +785,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
799
785
  const skillDest = join(cwd, ".agents", "skills", "patchcord");
800
786
  mkdirSync(skillDest, { recursive: true });
801
787
  writeFileSync(join(skillDest, "SKILL.md"),
802
- readFileSync(join(pluginRoot, "skills", "codex", "SKILL.md"), "utf-8"));
788
+ readFileSync(join(pluginRoot, "per-project-skills", "codex", "SKILL.md"), "utf-8"));
803
789
  const waitDest = join(cwd, ".agents", "skills", "patchcord-wait");
804
790
  mkdirSync(waitDest, { recursive: true });
805
791
  writeFileSync(join(waitDest, "SKILL.md"),
@@ -863,7 +849,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
863
849
  },
864
850
  }, null, 2) + "\n");
865
851
  writeFileSync(join(pluginDir, "skills", "patchcord", "SKILL.md"),
866
- readFileSync(join(pluginRoot, "skills", "codex", "SKILL.md"), "utf-8"));
852
+ readFileSync(join(pluginRoot, "per-project-skills", "codex", "SKILL.md"), "utf-8"));
867
853
  writeFileSync(join(pluginDir, "skills", "patchcord-wait", "SKILL.md"),
868
854
  readFileSync(join(pluginRoot, "skills", "wait", "SKILL.md"), "utf-8"));
869
855
 
@@ -887,7 +873,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd === "--token" || cmd ===
887
873
  const globalSkillDir = join(homedir(), ".agents", "skills", "patchcord");
888
874
  mkdirSync(globalSkillDir, { recursive: true });
889
875
  writeFileSync(join(globalSkillDir, "SKILL.md"),
890
- readFileSync(join(pluginRoot, "skills", "codex", "SKILL.md"), "utf-8"));
876
+ readFileSync(join(pluginRoot, "per-project-skills", "codex", "SKILL.md"), "utf-8"));
891
877
  const globalWaitDir = join(homedir(), ".agents", "skills", "patchcord-wait");
892
878
  mkdirSync(globalWaitDir, { recursive: true });
893
879
  writeFileSync(join(globalWaitDir, "SKILL.md"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.3.87",
3
+ "version": "0.3.89",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "author": "ppravdin",
6
6
  "license": "MIT",
@@ -26,6 +26,7 @@
26
26
  "hooks/",
27
27
  "scripts/",
28
28
  "skills/",
29
+ "per-project-skills/",
29
30
  "commands/",
30
31
  "README.md"
31
32
  ]
@@ -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.