patchcord 0.5.33 → 0.5.34
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 +21 -0
- package/package.json +1 -1
- package/skills/subscribe/SKILL.md +41 -132
package/bin/patchcord.mjs
CHANGED
|
@@ -68,6 +68,7 @@ Usage:
|
|
|
68
68
|
npx patchcord@latest --token <token> --server <url> Self-hosted with custom server
|
|
69
69
|
npx patchcord@latest --full Same + full statusline
|
|
70
70
|
npx patchcord@latest --rename <new-name> [--tool <slug>] Rename this agent (paste from dashboard)
|
|
71
|
+
npx patchcord@latest subscribe Start the realtime listener (used by /patchcord:subscribe)
|
|
71
72
|
npx patchcord@latest skill apply Fetch custom skill from web console`);
|
|
72
73
|
process.exit(0);
|
|
73
74
|
}
|
|
@@ -77,6 +78,26 @@ if (cmd === "plugin-path") {
|
|
|
77
78
|
process.exit(0);
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
// ── subscribe ─────────────────────────────────────────────────
|
|
82
|
+
// Thin wrapper around scripts/subscribe.mjs so users see a clean
|
|
83
|
+
// "npx patchcord subscribe" in their Claude Code tool log instead
|
|
84
|
+
// of a wall of bash. subscribe.mjs handles its own pidfile guard
|
|
85
|
+
// (exits with code 2 + "already running" if another listener is up),
|
|
86
|
+
// so this command needs zero pre-checks.
|
|
87
|
+
if (cmd === "subscribe") {
|
|
88
|
+
const subscribeScript = join(pluginRoot, "scripts", "subscribe.mjs");
|
|
89
|
+
if (!existsSync(subscribeScript)) {
|
|
90
|
+
console.error(`subscribe.mjs not found at ${subscribeScript}`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
const { spawnSync } = await import("child_process");
|
|
94
|
+
const result = spawnSync(process.execPath, [subscribeScript], {
|
|
95
|
+
stdio: "inherit",
|
|
96
|
+
env: process.env,
|
|
97
|
+
});
|
|
98
|
+
process.exit(result.status ?? (result.signal ? 1 : 0));
|
|
99
|
+
}
|
|
100
|
+
|
|
80
101
|
// ── --rename <new> [--tool <slug>] [--expect-token <prefix>] ────
|
|
81
102
|
// Renames the agent's bearer in its tool's per-project config. Supported
|
|
82
103
|
// tools: claude_code, codex, cursor, vscode, opencode. Dashboard generates
|
package/package.json
CHANGED
|
@@ -3,145 +3,54 @@ name: patchcord:subscribe
|
|
|
3
3
|
description: >
|
|
4
4
|
Start a background listener that wakes Claude the moment a new Patchcord
|
|
5
5
|
message arrives for this agent. Uses Supabase Realtime over WebSocket —
|
|
6
|
-
zero polling
|
|
7
|
-
|
|
8
|
-
/patchcord:subscribe.
|
|
6
|
+
zero polling. Use when the user says "subscribe", "listen for patchcord
|
|
7
|
+
messages", "wake me when messages arrive", or runs /patchcord:subscribe.
|
|
9
8
|
---
|
|
10
9
|
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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:
|
|
66
|
-
```
|
|
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>\""
|
|
72
|
-
)
|
|
73
|
-
```
|
|
74
|
-
No `2>&1`, no grep filter. By construction the script only writes
|
|
75
|
-
`PATCHCORD: ...` lines to stdout. Everything else — `connected`,
|
|
76
|
-
`token refreshed`, startup diagnostics, errors — goes to stderr,
|
|
77
|
-
which Monitor captures into its output file but does NOT fire as
|
|
78
|
-
notifications. So there's nothing to filter and no way to get the
|
|
79
|
-
filter wrong.
|
|
80
|
-
|
|
81
|
-
Crash detection is handled automatically by Monitor itself: when the
|
|
82
|
-
process exits, Monitor emits a built-in "stream ended" task
|
|
83
|
-
notification with the output file path and exit code.
|
|
84
|
-
|
|
85
|
-
6. **Tell the user one short line:**
|
|
86
|
-
"Patchcord listener active — I'll pick up new messages as they arrive."
|
|
10
|
+
# Start
|
|
11
|
+
|
|
12
|
+
Spawn under Monitor (not Bash with run_in_background — Monitor turns each stdout line into a notification):
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
Monitor(
|
|
16
|
+
description: "patchcord realtime listener",
|
|
17
|
+
persistent: true,
|
|
18
|
+
timeout_ms: 3600000,
|
|
19
|
+
command: "exec npx patchcord subscribe"
|
|
20
|
+
)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`subscribe.mjs` handles its own pidfile guard — if another listener is already active for this agent it exits with code 2 and stderr `already running (pid N)`. Monitor catches the stream-end event; read the output file and report.
|
|
24
|
+
|
|
25
|
+
Then one line to the user: *"Patchcord listener active — I'll pick up new messages as they arrive."*
|
|
87
26
|
|
|
88
27
|
# When a notification fires
|
|
89
28
|
|
|
90
|
-
Monitor surfaces `PATCHCORD: 1 new from <sender
|
|
29
|
+
Monitor surfaces `PATCHCORD: 1 new from <sender>`:
|
|
91
30
|
|
|
92
|
-
1. Say
|
|
31
|
+
1. Say: *"Got a Patchcord ping from <sender> — checking inbox."*
|
|
93
32
|
2. Call `mcp__patchcord__inbox`.
|
|
94
|
-
3.
|
|
95
|
-
patchcord:inbox skill), then reply with what you did.
|
|
96
|
-
4. Return to listening — Monitor keeps running.
|
|
33
|
+
3. Do the work per the patchcord:inbox skill, reply with what you did.
|
|
97
34
|
|
|
98
35
|
# Stopping
|
|
99
36
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
that targets PIDs or process names.
|
|
121
|
-
- Do NOT modify, delete, or write to the pidfile yourself. The script
|
|
122
|
-
manages it; it's already cleaned up by the time Monitor emits the
|
|
123
|
-
stream-end event.
|
|
124
|
-
- Do NOT spawn another Monitor or another `node subscribe.mjs`. One
|
|
125
|
-
failure means something is wrong with the config or environment;
|
|
126
|
-
respawning will not fix it and will make things worse.
|
|
127
|
-
- Do NOT search for orphaned processes or try to "clean up" state.
|
|
128
|
-
|
|
129
|
-
Concrete failure strings you may see and what they mean:
|
|
130
|
-
|
|
131
|
-
- `no .mcp.json in <cwd>` — session is not in a patchcord project dir.
|
|
132
|
-
Tell the user which directory to `cd` into.
|
|
133
|
-
- `ticket: token rejected (HTTP 401|403)` — bearer in `.mcp.json` is
|
|
134
|
-
bad; regenerate from the dashboard.
|
|
135
|
-
- `ticket: server not configured for realtime` — the patchcord server
|
|
136
|
-
hasn't had `SUPABASE_JWT_SECRET` / `SUPABASE_ANON_KEY` set. This is
|
|
137
|
-
a cloud-only feature.
|
|
138
|
-
- `ticket: namespace not owned — regenerate your token` — the token's
|
|
139
|
-
namespace lost its owner row; regenerate from the dashboard.
|
|
140
|
-
- `already running (pid N)` (exit code 2) — pidfile guard tripped,
|
|
141
|
-
another listener is active. Report and stop. Do NOT kill the other
|
|
142
|
-
listener to make room.
|
|
143
|
-
- `subscribe: fatal: ...` — unhandled error. Show the user the line
|
|
144
|
-
verbatim, stop.
|
|
145
|
-
|
|
146
|
-
If the process exited cleanly (exit 0) with no error line, the user
|
|
147
|
-
closed the session or killed the process. Nothing to do.
|
|
37
|
+
Tell the user one of:
|
|
38
|
+
- Close this Claude Code session.
|
|
39
|
+
- `kill $(cat /tmp/patchcord_subscribe_<namespace>_<agent>.pid)`
|
|
40
|
+
|
|
41
|
+
# If the Monitor stream ends
|
|
42
|
+
|
|
43
|
+
Read the output file. Scan the last ~15 lines for one of:
|
|
44
|
+
|
|
45
|
+
- `no .mcp.json in <cwd>` — session is not in a patchcord project dir
|
|
46
|
+
- `ticket: token rejected (HTTP 401|403)` — bad bearer; user regenerates from dashboard
|
|
47
|
+
- `ticket: server not configured for realtime` — self-hosted without Supabase
|
|
48
|
+
- `ticket: namespace not owned` — token lost its owner; regenerate
|
|
49
|
+
- `already running (pid N)` (exit 2) — another listener is active; report and stop
|
|
50
|
+
- `subscribe: fatal: ...` — surface the line verbatim
|
|
51
|
+
|
|
52
|
+
Report the cause in one sentence. STOP.
|
|
53
|
+
|
|
54
|
+
**Forbidden on failure:** no `pgrep`/`ps`/`kill`/`pkill`/`killall`, no pidfile writes, no respawning. The script manages pidfile cleanup itself; respawning will not fix a config problem.
|
|
55
|
+
|
|
56
|
+
Clean exit (code 0) with no error line = the user closed the session or killed the process. Nothing to do.
|