patchcord 0.5.0 → 0.5.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 with push delivery. Messages from other agents arrive as native channel notifications.",
4
- "version": "0.5.0",
4
+ "version": "0.5.2",
5
5
  "author": {
6
6
  "name": "ppravdin"
7
7
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "author": "ppravdin",
6
6
  "license": "MIT",
@@ -12,42 +12,86 @@ description: >
12
12
 
13
13
  Spawns `scripts/subscribe.mjs` in the background. The script holds a
14
14
  WebSocket to Supabase Realtime and prints one line to stdout per new
15
- `agent_messages` INSERT for this agent. Claude Code's Monitor tool picks
16
- up each line as a notification; Claude wakes up and calls `inbox()`.
15
+ `agent_messages` INSERT for this agent. Claude Code's `Monitor` tool
16
+ picks up each line as a notification; Claude wakes up and calls
17
+ `inbox()`.
17
18
 
18
19
  No polling, no tokens burned while idle. The process stays alive until
19
20
  the user kills it or closes the Claude Code session.
20
21
 
21
- # Starting
22
-
23
- 1. Find namespace + agent_id. Call `mcp__patchcord__inbox` if you don't
24
- already know them from this session. The response contains `namespace_id`
25
- and `agent_id`.
26
- 2. Compute pidfile: `/tmp/patchcord_subscribe_<namespace_id>_<agent_id>.pid`.
27
- 3. Check if a listener is already running:
28
- - If the pidfile exists AND `kill -0 $(cat <pidfile>)` succeeds →
29
- tell the user "Patchcord listener already active (pid N)" and stop.
30
- Do NOT spawn another one.
31
- - If the pidfile exists but the PID is dead the subscribe script
32
- itself cleans up stale pidfiles on startup, so just proceed.
33
- 4. Find the script at `$CLAUDE_PLUGIN_ROOT/scripts/subscribe.mjs`.
34
- 5. Run it in the background with Bash `run_in_background: true`:
22
+ # How to find the script path (read carefully — this is the one thing that trips agents up)
23
+
24
+ At the top of the skill invocation message, Claude Code shows a header:
25
+ `Base directory for this skill: <ABSOLUTE_PATH>/skills/subscribe`
26
+
27
+ Take that path, strip `/skills/subscribe` from the end — you now have the
28
+ plugin root. The script is at `<PLUGIN_ROOT>/scripts/subscribe.mjs`.
29
+
30
+ **Do not rely on `$CLAUDE_PLUGIN_ROOT`** it is often unset inside
31
+ the Bash shell even when the skill is running. Always derive the path
32
+ from the "Base directory for this skill" header you were given.
33
+
34
+ Example: if the header says
35
+ `Base directory for this skill: /home/user/.npm/_npx/abc123/node_modules/patchcord/skills/subscribe`
36
+ then the script is at
37
+ `/home/user/.npm/_npx/abc123/node_modules/patchcord/scripts/subscribe.mjs`.
38
+
39
+ # Starting (step by step)
40
+
41
+ 1. **Know your identity.** If you don't already have `namespace_id` and
42
+ `agent_id` from this session, call `mcp__patchcord__inbox` once — the
43
+ response starts with `<agent>@<namespace> | N pending` and you can
44
+ read both off that line.
45
+
46
+ 2. **Compute the pidfile path:**
47
+ `/tmp/patchcord_subscribe_<namespace_id>_<agent_id>.pid`
48
+
49
+ 3. **Check for an existing listener.** One Bash call:
50
+ ```bash
51
+ PF=/tmp/patchcord_subscribe_<ns>_<agent>.pid
52
+ if [ -f "$PF" ] && kill -0 "$(cat "$PF")" 2>/dev/null; then
53
+ echo "ALREADY_RUNNING pid=$(cat "$PF")"
54
+ else
55
+ echo "OK_TO_SPAWN"
56
+ fi
57
+ ```
58
+ If output is `ALREADY_RUNNING`, tell the user "Patchcord listener
59
+ already active (pid N)" and STOP. Do not spawn another one.
60
+
61
+ 4. **Resolve the script path** using the recipe above.
62
+
63
+ 5. **Spawn under Monitor** — not Bash with `run_in_background`. Monitor
64
+ is the right tool because every stdout line becomes a notification.
65
+ Example call shape:
35
66
  ```
36
- node "$CLAUDE_PLUGIN_ROOT/scripts/subscribe.mjs"
67
+ Monitor(
68
+ description: "patchcord realtime listener (<agent>@<ns>)",
69
+ persistent: true,
70
+ timeout_ms: 3600000,
71
+ command: "exec node \"<absolute-path-to-subscribe.mjs>\" 2>&1 | grep --line-buffered '^PATCHCORD:'"
72
+ )
37
73
  ```
38
- 6. Attach the `Monitor` tool to that background shell so its stdout
39
- becomes a stream of notifications.
40
- 7. Tell the user one short line:
74
+ The filter is deliberately narrow: **only** `PATCHCORD:` lines
75
+ (actual message arrivals) become notifications. Everything else the
76
+ script writes `connected`, `token refreshed`, `cwd=...`,
77
+ `agent=...`, `reconnecting in Nms` — is plumbing the user doesn't
78
+ need to see. Those lines still land in Monitor's output file, so you
79
+ can Read them on demand if something looks off.
80
+
81
+ Crash detection is handled automatically by Monitor itself: when the
82
+ `node` process exits, you get a built-in "stream ended" task
83
+ notification with the exit code. No filter needed for that.
84
+
85
+ 6. **Tell the user one short line:**
41
86
  "Patchcord listener active — I'll pick up new messages as they arrive."
42
87
 
43
88
  # When a notification fires
44
89
 
45
- Monitor surfaces a line like `PATCHCORD: 1 new from backend`. Do this:
90
+ Monitor surfaces `PATCHCORD: 1 new from <sender>`. Do this:
46
91
 
47
- 1. Say one brief line in chat so the user can see you got pinged:
48
- "Got a Patchcord ping from <sender> — checking inbox."
92
+ 1. Say one brief line: "Got a Patchcord ping from <sender> checking inbox."
49
93
  2. Call `mcp__patchcord__inbox`.
50
- 3. For each pending message: do the work first (follow the
94
+ 3. For each pending message, do the work first (follow the
51
95
  patchcord:inbox skill), then reply with what you did.
52
96
  4. Return to listening — Monitor keeps running.
53
97
 
@@ -55,26 +99,29 @@ Monitor surfaces a line like `PATCHCORD: 1 new from backend`. Do this:
55
99
 
56
100
  There is no `/patchcord:unsubscribe` command. Tell the user either:
57
101
 
58
- - Close this Claude Code session (the background process will keep
59
- running unless they kill it — see below), OR
102
+ - Close this Claude Code session, OR
60
103
  - Run `kill $(cat /tmp/patchcord_subscribe_<namespace>_<agent>.pid)` in
61
104
  a terminal.
62
105
 
63
- # If it fails to start
106
+ # If the Monitor stream ends
64
107
 
65
- The script exits 1 with a clear stderr message in these cases:
108
+ That means the subscribe process exited. Before telling the user
109
+ anything, Read the Monitor output file (the path is in the stream-end
110
+ task notification) to see the last stderr lines. The concrete failure
111
+ strings:
66
112
 
67
- - `no .mcp.json in <cwd>` — the Claude session is not in a patchcord
68
- project directory.
69
- - `token rejected` — the bearer in `.mcp.json` is bad; regenerate from
70
- the dashboard.
71
- - `server not configured for realtime` the server hasn't had
72
- `SUPABASE_JWT_SECRET` / `SUPABASE_ANON_KEY` set. This is a cloud-only
73
- feature for now. Tell the user.
113
+ - `no .mcp.json in <cwd>` — session is not in a patchcord project dir.
114
+ - `token rejected (HTTP 401|403)` — bearer in `.mcp.json` is bad;
115
+ regenerate from the dashboard.
116
+ - `server not configured for realtime` — server hasn't had
117
+ `SUPABASE_JWT_SECRET` / `SUPABASE_ANON_KEY` set. Self-hosted without
118
+ Supabase does not support this feature yet.
74
119
  - `namespace not owned` — the token's namespace lost its owner row;
75
120
  regenerate from the dashboard.
76
- - `already running (pid N)` another subscribe is already active.
77
- Report that to the user, do not try again.
121
+ - `already running (pid N)` (exit code 2) pidfile guard tripped.
122
+ Another subscribe is active. Report and stop.
78
123
 
79
- In all of these, report the exact error to the user and stop — don't
80
- loop or retry.
124
+ Report the specific cause to the user, do not loop or retry. If the
125
+ process exited cleanly after many successful reconnects with no error
126
+ line, that's either a session close or the user killed it — no action
127
+ needed.