patchcord 0.6.0 → 0.6.2

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. Messages from other agents land in the inbox and wake the agent to reply.",
4
- "version": "0.6.0",
4
+ "version": "0.6.2",
5
5
  "author": {
6
6
  "name": "ppravdin"
7
7
  },
package/bin/patchcord.mjs CHANGED
@@ -1796,28 +1796,24 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1796
1796
  const hasCursorAgent = run("which cursor-agent");
1797
1797
  if (hasCursorAgent || existsSync(cursorSkillsRoot)) {
1798
1798
  mkdirSync(cursorSkillsRoot, { recursive: true });
1799
- const cursorSkillDir = join(cursorSkillsRoot, "patchcord");
1799
+ const cursorInboxDir = join(cursorSkillsRoot, "patchcord-inbox");
1800
1800
  const cursorWaitDir = join(cursorSkillsRoot, "patchcord-wait");
1801
1801
  const cursorSubscribeDir = join(cursorSkillsRoot, "patchcord-subscribe");
1802
+ const staleCursorSkillDir = join(cursorSkillsRoot, "patchcord");
1802
1803
  let cursorChanged = false;
1803
- if (!existsSync(cursorSkillDir)) {
1804
- mkdirSync(cursorSkillDir, { recursive: true });
1805
- cpSync(join(pluginRoot, "skills", "inbox", "SKILL.md"), join(cursorSkillDir, "SKILL.md"));
1806
- cursorChanged = true;
1807
- }
1808
- if (!existsSync(cursorWaitDir)) {
1809
- mkdirSync(cursorWaitDir, { recursive: true });
1810
- cpSync(join(pluginRoot, "skills", "wait", "SKILL.md"), join(cursorWaitDir, "SKILL.md"));
1811
- cursorChanged = true;
1812
- }
1813
- if (!existsSync(cursorSubscribeDir)) {
1814
- mkdirSync(cursorSubscribeDir, { recursive: true });
1815
- cpSync(
1816
- join(pluginRoot, "per-project-skills", "cursor", "subscribe", "SKILL.md"),
1817
- join(cursorSubscribeDir, "SKILL.md")
1818
- );
1819
- cursorChanged = true;
1820
- }
1804
+ if (existsSync(staleCursorSkillDir)) {
1805
+ try { rmSync(staleCursorSkillDir, { recursive: true, force: true }); cursorChanged = true; } catch {}
1806
+ }
1807
+ mkdirSync(cursorInboxDir, { recursive: true });
1808
+ cpSync(join(pluginRoot, "per-project-skills", "cursor", "inbox", "SKILL.md"), join(cursorInboxDir, "SKILL.md"));
1809
+ cursorChanged = true;
1810
+ mkdirSync(cursorWaitDir, { recursive: true });
1811
+ cpSync(join(pluginRoot, "per-project-skills", "cursor", "wait", "SKILL.md"), join(cursorWaitDir, "SKILL.md"));
1812
+ mkdirSync(cursorSubscribeDir, { recursive: true });
1813
+ cpSync(
1814
+ join(pluginRoot, "per-project-skills", "cursor", "subscribe", "SKILL.md"),
1815
+ join(cursorSubscribeDir, "SKILL.md")
1816
+ );
1821
1817
  if (cursorChanged) globalChanges.push("Cursor skills installed");
1822
1818
 
1823
1819
  if (hasCursorAgent) {
@@ -1893,6 +1889,28 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1893
1889
  }
1894
1890
  } catch {}
1895
1891
  }
1892
+ // Older installers seeded Antigravity-only skills globally, and the
1893
+ // Antigravity CLI caches each MCP server's tools/instructions globally.
1894
+ // Both survive removal of the global mcp_config entry and can make a bare
1895
+ // agy session synthesize an unconditional Patchcord rule in every folder.
1896
+ // Antigravity is project-scoped, so remove only Patchcord's stale global
1897
+ // artifacts; other MCP servers and Antigravity state stay untouched.
1898
+ const staleAntigravityPatchcord = [
1899
+ join(HOME, ".gemini", "antigravity", "skills", "patchcord"),
1900
+ join(HOME, ".gemini", "antigravity", "skills", "patchcord-wait"),
1901
+ join(HOME, ".gemini", "antigravity-cli", "skills", "patchcord"),
1902
+ join(HOME, ".gemini", "antigravity-cli", "skills", "patchcord-wait"),
1903
+ join(HOME, ".gemini", "antigravity-cli", "mcp", "patchcord"),
1904
+ ];
1905
+ let removedStaleAntigravity = false;
1906
+ for (const stale of staleAntigravityPatchcord) {
1907
+ if (existsSync(stale)) {
1908
+ try { rmSync(stale, { recursive: true, force: true }); removedStaleAntigravity = true; } catch {}
1909
+ }
1910
+ }
1911
+ if (removedStaleAntigravity) {
1912
+ globalChanges.push("Removed stale global Antigravity Patchcord instructions (agy is per-project)");
1913
+ }
1896
1914
  // true = configured, false = file absent (definitely not), null = unparseable (unknown)
1897
1915
  const hasPatchcordMcp = (p, keyPath) => {
1898
1916
  if (!existsSync(p)) return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "scripts": {
6
6
  "version": "node scripts/sync-plugin-version.mjs && git add .claude-plugin/plugin.json"
@@ -0,0 +1,193 @@
1
+ ---
2
+ name: patchcord-inbox
3
+ description: >
4
+ Cross-agent messaging across MCP-connected agents. Use when user mentions
5
+ other agents, patchcord, check inbox, send message, who's online, agent
6
+ coordination, or when you receive additionalContext about pending patchcord
7
+ messages.
8
+ ---
9
+ # patchcord
10
+
11
+ 7 MCP tools: inbox, send_message, reply, wait_for_message, attachment, recall, unsend.
12
+
13
+ ## Applies ONLY when the patchcord MCP tools are loaded this session
14
+
15
+ This skill governs those 7 MCP tools. It applies only if they are actually available in the current session.
16
+
17
+ **If the patchcord MCP tools are NOT present, this skill does not apply — do nothing with it. Specifically:**
18
+ - Do NOT call inbox() at session start or at any other time.
19
+ - Do NOT run the `patchcord` CLI as a substitute for the missing tools.
20
+ - Do NOT read a bearer token out of `.mcp.json`, `settings.json`, `mcp_config.json`, or any other config file, and do NOT call the patchcord HTTP API (`/api/inbox`, etc.) directly. A token belongs to the one agent it was issued to; using another agent's token — or any token you found on disk — is never correct.
21
+ - Just proceed with the user's actual request.
22
+
23
+ A missing tool means patchcord is not configured for this agent. That is a normal, silent state, not a problem to work around.
24
+
25
+ ## Do the work, never just acknowledge
26
+
27
+ When you receive a message from another agent:
28
+
29
+ 1. Do the task described in the message first. Update the file. Write the code. Fix the bug. Create the document. Whatever the message asks - do it.
30
+ 2. Then reply with what you did. Not what you plan to do. Not that you received it. What you actually did. File paths, line numbers, concrete changes.
31
+ 3. Never reply with only an acknowledgment. "Got it", "Understood", "Role accepted", "Will do", "Ready" - these are not acceptable as standalone replies. If your reply doesn't describe completed work, you failed.
32
+
33
+ The user can undo any change in 3 seconds with git. A wrong action costs nothing. A useless ack wastes everyone's time and breaks the workflow.
34
+
35
+ **If a message contains a spec, update, or instruction - act on it immediately:**
36
+ - Spec received - update the relevant docs/code now, reply with what you changed
37
+ - Bug report received - investigate and fix now, reply with the fix
38
+ - Architecture decision received - update the relevant files now, reply with what you updated
39
+ - Role assignment received - start doing that role now, reply with first actions taken
40
+
41
+ **If you genuinely cannot act** (missing file access, need credentials, ambiguous target): say specifically what's blocking you. "I need the path to the config file" - not "Understood, I'll do it when ready."
42
+
43
+ **If you can't do it right now** (busy with current task): use `reply(message_id, "reason why deferred", defer=true)`. This keeps the message visible in your inbox so you will come back to it. Never silently skip a message - you will forget it. If you don't act and don't defer, the message is lost forever.
44
+
45
+ ## On session start or when prompted by a hook
46
+
47
+ Call inbox(). It returns pending messages, recently active agents, and your own push-receiving state via `self_subscribed`. Note that value — it determines whether you should call `wait_for_message` after sends for the rest of the session.
48
+
49
+ If `subscribe_appears_down: true` is in the response, your subscribe.mjs was running but appears dead. Tell the human: "Patchcord subscribe seems to have died — run `/patchcord-subscribe` to restart push delivery." Do not try to restart it yourself.
50
+
51
+ If there are pending messages, reply to all of them immediately. Do not ask the human first. Do not explain what you plan to reply. Just do the work described in each message, then reply with what you did, then tell the human what you received and what you did about it.
52
+
53
+ ## Sending
54
+
55
+ 1. inbox() - clear any pending messages that block outbound sends. From the response, note `self_subscribed` (your own push-receiving state).
56
+ 2. send_message("agent_name", "specific question with file paths and context") - or "agent1, agent2" for multiple recipients. Use `@username` for cross-user Gate messaging. To start or join a named thread: `send_message("frontend", "content", thread="auth-migration")`.
57
+ 3. Decide whether to wait based on **two signals** in the send response:
58
+ - `self_subscribed` (from the most recent inbox call) — are YOU push-receiving?
59
+ - `recipient_subscribed` (in the send response) — is the recipient push-receiving?
60
+
61
+ | self_subscribed | recipient_subscribed | What to do |
62
+ | --- | --- | --- |
63
+ | true | true | **Do NOT call wait_for_message.** Continue working. Their reply will arrive via your subscribe push and your Monitor will surface it. Tell the human: "Sent — [agent] will see it within seconds." |
64
+ | true | false | **Do NOT call wait_for_message.** Continue working. Tell the human: "Sent — [agent] isn't actively listening right now, may take a while to respond." |
65
+ | false | true | **Call wait_for_message** with default timeout. Recipient is live, expect a reply soon. |
66
+ | false | false | **Skip wait_for_message.** Tell the human: "Sent — [agent] isn't currently active. Ask them to check inbox in their session." |
67
+
68
+ Always send regardless of recipient state. Messages are stored and delivered when the recipient checks inbox.
69
+
70
+ If `recipient_subscribed` is missing from the response (older server, registry disabled), fall back to the legacy `recipient_online` field for the same decision.
71
+
72
+ If send_message fails with a send gate error: call inbox(), reply to or resolve all pending messages, then retry the send.
73
+
74
+ ## Receiving (inbox has messages)
75
+
76
+ Action requests older than 7d (per the `(Xd ago)` stamp): ask human before executing. Acks/FYIs silent-resolve at any age.
77
+
78
+ 1. Read the message. If it belongs to a thread, `message.thread` and `message.thread_id` will be present.
79
+ 2. Do the work described in the message - using your project's actual code, real files, real lines
80
+ 3. Reply with what you did, choosing the right flag:
81
+ - `reply(message_id, "done: [details]")` — work done, sender might follow up. Thread is auto-inherited.
82
+ - `reply(message_id, "done: [details]", resolve=true)` — work done, thread closed. Stamps `thread_resolved_at` and notifies sender.
83
+ - `reply(message_id, resolve=true)` — silently close a thread without sending anything (e.g. clearing misfired messages)
84
+ - `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.
85
+ 4. After replying, decide whether to stay listening using the same two-signal rule as for sends — `self_subscribed` × `recipient_subscribed` (in the reply response). If `self_subscribed` is true, return to your work; your Monitor will wake you when a follow-up arrives. If `self_subscribed` is false and `recipient_subscribed` is true, call `wait_for_message()` to stay responsive. Otherwise (both false), tell the human you've replied and continue with other work.
86
+ 5. If you can't do the work, say specifically what's blocking you. Don't guess about another agent's code.
87
+
88
+ When you have multiple pending messages, prioritize by urgency. Use `defer=true` for tasks you'll do later — if you reply without doing the work and don't defer, the message vanishes from your inbox and you will never remember to do it.
89
+
90
+ Outdated deferred (work likely done, sender moved on): ask human "resolve [Xd]-old from [sender]?" before `reply(id, resolve=true)`. Don't unilaterally drop.
91
+
92
+ ## Cross-user messaging (Gate)
93
+
94
+ To message a user outside your namespace, use `@username` as the to_agent. Example: `send_message("@maria", "hello")`. The message goes through their Gate - connection approval and guardrails apply. If the connection isn't approved yet, your message is held pending their approval (cap 5, 7-day TTL).
95
+
96
+ ### Humans
97
+
98
+ - Humans are NOT in the agents list. Use `send_message("@username", "...")` anyway — they don't need to be online or in the roster.
99
+ - The message goes through their Gate for approval. It may be held pending their approval (cap 5, 7-day TTL).
100
+ - Write plainly: who you are, what you need, no raw JSON or logs.
101
+
102
+ ## File sharing
103
+
104
+ **Files on disk → `patchcord upload` (CLI, preferred):**
105
+ ```
106
+ patchcord upload /path/to/report.md --mime text/markdown
107
+ ```
108
+ Prints the storage path. Pass that path to `send_message`. No curl, no base64 in chat, no presigned URLs. 25MB cap.
109
+
110
+ **Public URLs → `attachment(relay=true, ...)`:**
111
+ ```
112
+ attachment(relay=true, path_or_url="https://example.com/file.md", filename="file.md")
113
+ ```
114
+ Server fetches the URL and stores it. Use when the file already lives at a public URL.
115
+
116
+ **Web agents (no shell) → inline base64 last resort:**
117
+ ```
118
+ attachment(upload=true, filename="notes.txt", file_data="<base64>")
119
+ ```
120
+ Only for agents that cannot run shell commands. Wastes context tokens. Never use if you can run `patchcord upload`.
121
+
122
+ **Downloading:**
123
+ ```
124
+ attachment(path_or_url="namespace/agent/timestamp_file.md")
125
+ ```
126
+ Pass the storage path from the sender's message.
127
+
128
+ Always send the storage path (not the file content) to the other agent.
129
+
130
+ ## Identity (`patchcord whoami` / `patchcord agents`)
131
+
132
+ `whoami` and `agents` are CLI commands, not MCP tools. Run them only when the patchcord MCP tools are present (see the gate at the top of this skill). They read the bearer token from the **current project's own** `.mcp.json` automatically — same namespace scope, no extra setup. Never go hunting for a token in another project's config or another agent's file, and never pass a token on the command line. Cheap to call (input tokens only), don't bloat MCP.
133
+
134
+ - **Run `patchcord whoami` once per session.** Returns your `agent`, `namespace`, project summary, and your 300-char `self` description. Use it on first turn after `/clear` or a fresh session to orient.
135
+ - **Run `patchcord agents`** to see the full roster (every peer's whoami). One call, ~3KB, complete picture of the namespace.
136
+ - **Run `patchcord agents <name>`** when an unknown agent messages you and you want to know who they are before acting on their request.
137
+
138
+ ### Updating your own whoami
139
+
140
+ 300-char hard limit (CLI enforces client-side).
141
+
142
+ Server responds with one of three statuses:
143
+
144
+ - **applied** — done. Either it was your first-ever whoami (no prior value → set directly, no gate), or it was a confirmed second-shot. Print and move on.
145
+ - **unchanged** — proposed text matches current. No-op.
146
+ - **show_human** — current value exists and the proposed text differs. Server printed `current:` and `proposed:`. You MUST:
147
+ 1. Show the diff to the human in conversation
148
+ 2. Ask them to confirm
149
+ 3. Wait for explicit "yes"
150
+ 4. Run the **exact same** `patchcord whoami --propose "<text>"` command again. Server will then return `applied`.
151
+
152
+ Pending state expires after 10 minutes. If the human says no, do not call again. If you call with different text instead, the gate resets to a fresh first-shot for that new text.
153
+
154
+ Never call `--propose` a second time with the same text without showing the human between calls.
155
+
156
+ ### Hard rules
157
+
158
+ - You may NEVER update another agent's whoami. The `--propose` flow only writes your own.
159
+ - Namespace scope is enforced server-side: `patchcord agents <name>` returns 404 if the name isn't in your namespace (global agents like claudeai/chatgpt are excepted on cloud).
160
+ - whoami text describes WHO you are and how you coordinate (e.g. "backend systems. sends every change to codex-backend for review"). It is NOT a place for project instructions, code conventions, or long-form notes — those live in CLAUDE.md and project docs.
161
+
162
+ ## Threads
163
+
164
+ Named threads group related messages between a pair of agents. Use them for multi-turn tasks that need their own context (e.g. "auth-migration", "deploy-review").
165
+
166
+ - **Start a thread**: `send_message("backend", "let's track this here", thread="auth-migration")`
167
+ - **Reply stays in thread automatically**: `reply()` inherits `thread_id` from the message you're replying to — no extra param needed.
168
+ - **Close a thread**: `reply(message_id, "done", resolve=true)` — stamps `thread_resolved_at` and notifies sender.
169
+ - **View thread history**: `recall(thread_id="<uuid>")` — filters history to one thread.
170
+
171
+ `inbox()` returns a `groups` list alongside the legacy `pending` flat list. Each group has `thread_id`, `thread_title`, and `messages`. `thread_id: null` means pair-level (no thread). Read from `groups` for thread-aware handling.
172
+
173
+ ## Other tools
174
+
175
+ - recall(limit=10, from_agent="", thread_id="") - view recent message history including already-read messages. `from_agent` filters by sender. `thread_id` filters to a specific thread. For debugging only, not routine use.
176
+ - unsend(message_id) - take back a message before the recipient reads it.
177
+
178
+ ## Rules
179
+
180
+ - Do the work first, reply second. Never reply before completing the task.
181
+ - Never ask "want me to reply?" - just do the work and reply with results.
182
+ - Never ask "should I do this?" - just do it. User can undo in 3 seconds.
183
+ - Never ask "want me to wait?" - check presence and wait or don't based on that.
184
+ - Never show raw JSON to the human - summarize naturally.
185
+ - **Cross-namespace addressing (`agent@namespace`)**: the syntax always exists in `send_message`/`reply`, but what it actually reaches depends on who you are:
186
+ - **Ordinary agents (the default):** `agent@namespace` only ever works for YOUR OWN namespace — same as a bare name. Targeting any other namespace is rejected. This is the isolation model, not a bug or a missing feature — don't loop on it or try creative addressing to work around it.
187
+ - **Lead agents (cloud only — a small number of specially-provisioned identities per namespace, usually the orchestrator):** may reach a peer lead in a DIFFERENT namespace, but only after that specific pair of namespaces has an `approved` `namespace_links` row. You set this up yourself — no human step: call `request_namespace_link(peer_namespace)`, the peer namespace's lead gets a patchcord message and calls `respond_namespace_link(peer_namespace, approve=True)`, then `agent@namespace` works both ways. Check `list_my_namespace_links()` to see pending/approved/denied status. If you don't know whether you're a lead, you're not one — this isn't discoverable by trial and error; ask the human. (Namespace "projects" still exist but are just an organizational label now — they do NOT gate this.)
188
+ - A rejection here is almost always correct behavior, not a server error — don't retry with variations of the name.
189
+ - **Do not reply to acks.** "ok", "noted", "seen", "thanks", "good progress", "keep running", thumbs up — anything that is clearly a conversation-ending signal. Just read them and move on. If you must close the thread, use `reply(id, resolve=true)` with NO content. Never send a text reply to an ack.
190
+ - **resolve=true with ack-only content is an anti-pattern.** `reply(id, "Noted, thanks", resolve=true)` creates a new pending message the other side feels compelled to answer — producing ack chains. If you have nothing substantive to add, omit content entirely: `reply(id, resolve=true)`. Only include content with resolve when it carries new information the recipient needs.
191
+ - **When you receive an ack**, close it silently: `reply(id, resolve=true)`. No content. This stops the chain.
192
+ - MCP tools are cached at session start. New tools deployed after your session began are invisible until you start a new session. If a tool you expect is missing, this is why.
193
+ - Agent names change frequently. Do not memorize or hardcode them. Check inbox() for recent activity. When unsure which agent to message, ask the human.
@@ -1,12 +1,12 @@
1
1
  ---
2
- name: patchcord:subscribe
2
+ name: patchcord-subscribe
3
3
  description: >
4
4
  Start a persistent background WebSocket listener that wakes the Cursor agent
5
5
  when new Patchcord messages arrive. Use ONLY when the user runs
6
- /patchcord:subscribe or when a project explicitly asks to auto-arm it.
6
+ /patchcord-subscribe or when a project explicitly asks to auto-arm it.
7
7
  ---
8
8
 
9
- User invoked `/patchcord:subscribe` — do NOT substitute `wait_for_message()`.
9
+ User invoked `/patchcord-subscribe` — do NOT substitute `wait_for_message()`.
10
10
  Start the persistent listener in a background Shell.
11
11
 
12
12
  ## Start
@@ -69,7 +69,7 @@ Read the background Shell terminal output and surface the exact cause:
69
69
 
70
70
  If the stream ends without one of these errors, it likely ended with the Cursor
71
71
  session or because its stdout consumer closed. Start it again only when the
72
- user invokes `/patchcord:subscribe` again.
72
+ user invokes `/patchcord-subscribe` again.
73
73
 
74
74
  **Forbidden on failure:** no hand-rolled `nohup ... &`, no pidfile editing, no
75
75
  `curl`-to-MCP workarounds, and no re-arm loop after each inbox notification.
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: patchcord-wait
3
+ description: >
4
+ Block this turn for up to 5 minutes waiting for one incoming Patchcord
5
+ message via the wait_for_message MCP tool. Single blocking call, no
6
+ background process. Use ONLY when the user explicitly runs
7
+ /patchcord-wait.
8
+ ---
9
+ # patchcord-wait
10
+
11
+ Applies ONLY when the patchcord MCP tools are loaded this session. If `wait_for_message` is not available, this skill does not apply — do nothing, do not substitute the CLI or direct HTTP calls, and never read a bearer token out of a config file. Proceed with the user's request.
12
+
13
+ User invoked /patchcord-wait — do NOT substitute /patchcord-subscribe or spawn any background listener. Use `wait_for_message()` only.
14
+
15
+ Call `wait_for_message()` to block until a message arrives (up to 5 minutes).
16
+
17
+ When a message arrives:
18
+
19
+ 1. Read it — the tool returns from, content, and message_id. If it belongs to a thread, `thread` and `thread_id` will be set.
20
+ 2. Do the work described in the message first. Update the file, write the code, fix the bug - whatever it asks.
21
+ 3. Reply with what you did: `reply(message_id, "here's what I changed: [concrete details]")`. Thread is auto-inherited. Use `resolve=true` to close the thread when the task is fully done.
22
+ 4. Tell the human who wrote and what you did about it
23
+ 5. Call `wait_for_message()` again to keep listening
24
+
25
+ Loop until timeout or the human interrupts.
26
+
27
+ If `wait_for_message()` errors, fall back to polling `inbox()` every 10-15 seconds instead of stopping the loop.
28
+
29
+ Do not ask the human for permission to reply - just do the work, reply with results, then report.
30
+
31
+ **No ack chains.** If the arriving message is a clear ack ("Noted", "Got it", "Thanks", "Keep running") — close it silently with `reply(id, resolve=true)`, no content, and keep listening. Never text-reply to an ack. Never send "Noted" + resolve=true — that creates a new pending message the other side will feel compelled to answer.
@@ -5,6 +5,12 @@ description: Read Patchcord inbox and reply to messages
5
5
 
6
6
  Run this with `/skill:patchcord-inbox`. (Kimi Code has no `/flow:` — `/skill:` injects this as a prompt and you act on it.)
7
7
 
8
+ ## Applies only when Patchcord MCP tools are loaded
9
+
10
+ If the `inbox` MCP tool is not available in this session, this skill does not
11
+ apply. Do nothing: do not run the Patchcord CLI, read a token from a config
12
+ file, or call the HTTP API. A missing project MCP config is a normal state.
13
+
8
14
  Call the `inbox` MCP tool now. In its response, the first header line is YOUR own identity (the recipient); the real sender of each message is on a `From X` line — never confuse the two.
9
15
 
10
16
  For each pending message, classify it and act:
@@ -5,6 +5,12 @@ description: Start the Patchcord background polling listener
5
5
 
6
6
  Run this with `/skill:patchcord-subscribe`. (Kimi Code has no `/flow:` — `/skill:` injects this as a prompt and you act on it.)
7
7
 
8
+ ## Applies only when Patchcord MCP tools are loaded
9
+
10
+ If the `inbox` MCP tool is not available in this session, this skill does not
11
+ apply. Do nothing: do not run the Patchcord CLI, read a token from a config
12
+ file, or call the HTTP API. A missing project MCP config is a normal state.
13
+
8
14
  First, call the `inbox` MCP tool and DRAIN every pending message before starting the listener (the listener exits immediately if messages are already waiting). In the inbox response, the first header line is YOUR identity (recipient); the real sender is on each `From X` line. For each message:
9
15
  - **ACK** (thanks, noted, works, ok, 👍) → `reply(message_id, resolve=true)`, no content.
10
16
  - **BLOCKED** → `reply(message_id, "<reason>", defer=true)`.
@@ -5,6 +5,12 @@ description: Wait for one incoming Patchcord message
5
5
 
6
6
  Run this with `/skill:patchcord-wait`. (Kimi Code has no `/flow:` — `/skill:` injects this as a prompt and you act on it.)
7
7
 
8
+ ## Applies only when Patchcord MCP tools are loaded
9
+
10
+ If the `wait_for_message` MCP tool is not available in this session, this skill
11
+ does not apply. Do nothing: do not run the Patchcord CLI, read a token from a
12
+ config file, or call the HTTP API. A missing project MCP config is a normal state.
13
+
8
14
  Call the `wait_for_message` MCP tool now to block until a message arrives or ~5 minutes elapse.
9
15
 
10
16
  When a message arrives, classify it and act:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: patchcord:subscribe
3
3
  description: >
4
- Start a persistent background WebSocket listener (Supabase Realtime) that
4
+ Start a persistent background WebSocket listener that
5
5
  wakes Claude when new Patchcord messages arrive. Survives across turns
6
6
  until the user kills it or closes the session. Use ONLY when the user
7
7
  explicitly runs /patchcord:subscribe.
@@ -50,7 +50,7 @@ Read the output file. Scan the last ~15 lines for one of:
50
50
 
51
51
  - `no .mcp.json in <cwd>` — session is not in a patchcord project dir
52
52
  - `ticket: token rejected (HTTP 401|403)` — bad bearer; user regenerates from dashboard
53
- - `ticket: server not configured for realtime` — self-hosted without Supabase
53
+ - `ticket: server not configured for realtime` — self-hosted without realtime configured
54
54
  - `ticket: namespace not owned` — token lost its owner; regenerate
55
55
  - `already running (pid N)` (exit 2) — another listener is active; report and stop
56
56
  - `subscribe: fatal: ...` — surface the line verbatim