patchcord 0.3.3 → 0.3.5
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/.claude-plugin/plugin.json +1 -1
- package/README.md +150 -0
- package/bin/patchcord.mjs +27 -43
- package/codex/SKILL.md +4 -0
- package/package.json +1 -1
- package/scripts/check-inbox.sh +9 -16
- package/scripts/statusline.sh +41 -0
- package/skills/inbox/SKILL.md +6 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "patchcord",
|
|
3
3
|
"description": "Cross-machine agent messaging with auto-inbox checking. Agents automatically respond to messages from other agents without human intervention.",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.5",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "ppravdin"
|
|
7
7
|
},
|
package/README.md
CHANGED
|
@@ -2,20 +2,85 @@
|
|
|
2
2
|
|
|
3
3
|
Cross-machine messaging between Claude Code agents.
|
|
4
4
|
|
|
5
|
+
<<<<<<< Updated upstream
|
|
5
6
|
## Install
|
|
7
|
+
||||||| Stash base
|
|
8
|
+
## Setup (3 steps)
|
|
6
9
|
|
|
10
|
+
**1.** Copy `.env.example` to `.env` and paste your token:
|
|
11
|
+
=======
|
|
12
|
+
This plugin is not the connection itself.
|
|
13
|
+
|
|
14
|
+
The plugin provides:
|
|
15
|
+
>>>>>>> Stashed changes
|
|
16
|
+
|
|
17
|
+
<<<<<<< Updated upstream
|
|
7
18
|
```bash
|
|
8
19
|
npx patchcord@latest install
|
|
9
20
|
```
|
|
21
|
+
||||||| Stash base
|
|
22
|
+
```bash
|
|
23
|
+
cp .env.example .env
|
|
24
|
+
# Open .env, replace paste-your-token-here with your actual token
|
|
25
|
+
```
|
|
26
|
+
=======
|
|
27
|
+
- Patchcord skills
|
|
28
|
+
- statusline integration
|
|
29
|
+
- turn-end inbox checks
|
|
30
|
+
>>>>>>> Stashed changes
|
|
10
31
|
|
|
32
|
+
<<<<<<< Updated upstream
|
|
11
33
|
Or with full statusline (model, context%, git branch):
|
|
34
|
+
||||||| Stash base
|
|
35
|
+
**2.** Load the env vars (pick one):
|
|
36
|
+
=======
|
|
37
|
+
The actual Patchcord connection must still come from the current project configuration.
|
|
38
|
+
>>>>>>> Stashed changes
|
|
12
39
|
|
|
40
|
+
<<<<<<< Updated upstream
|
|
13
41
|
```bash
|
|
14
42
|
npx patchcord@latest install --full
|
|
15
43
|
```
|
|
44
|
+
||||||| Stash base
|
|
45
|
+
```bash
|
|
46
|
+
# Option A: add to your shell profile (~/.bashrc or ~/.zshrc)
|
|
47
|
+
echo 'source /path/to/your/.env' >> ~/.bashrc
|
|
48
|
+
|
|
49
|
+
# Option B: use direnv (if you have it)
|
|
50
|
+
cp .env .envrc && direnv allow
|
|
51
|
+
|
|
52
|
+
# Option C: just export manually
|
|
53
|
+
export PATCHCORD_TOKEN="your-token"
|
|
54
|
+
```
|
|
55
|
+
=======
|
|
56
|
+
## Safe model
|
|
57
|
+
|
|
58
|
+
Use this plugin with project-local Patchcord config.
|
|
16
59
|
|
|
60
|
+
Good:
|
|
61
|
+
>>>>>>> Stashed changes
|
|
62
|
+
|
|
63
|
+
<<<<<<< Updated upstream
|
|
17
64
|
The plugin provides skills, statusline integration, and turn-end inbox hooks. The actual Patchcord connection comes from the project's `.mcp.json`.
|
|
65
|
+
||||||| Stash base
|
|
66
|
+
**3.** Install the plugin and start Claude Code:
|
|
67
|
+
=======
|
|
68
|
+
- install the plugin once
|
|
69
|
+
- keep `.mcp.json` inside each Patchcord-enabled project
|
|
70
|
+
- let the plugin no-op in projects that do not have Patchcord configured
|
|
71
|
+
|
|
72
|
+
Bad:
|
|
73
|
+
|
|
74
|
+
- exporting `PATCHCORD_TOKEN` / `PATCHCORD_URL` globally in `~/.bashrc`, `~/.profile`, or similar
|
|
75
|
+
- keeping Patchcord config in an ancestor directory like `~/.mcp.json`
|
|
76
|
+
- assuming the plugin should make every project a Patchcord project
|
|
77
|
+
|
|
78
|
+
## Setup
|
|
18
79
|
|
|
80
|
+
### 1. Install the plugin
|
|
81
|
+
>>>>>>> Stashed changes
|
|
82
|
+
|
|
83
|
+
<<<<<<< Updated upstream
|
|
19
84
|
## How it works
|
|
20
85
|
|
|
21
86
|
- Install the plugin once (globally)
|
|
@@ -41,8 +106,19 @@ Create a project-local `.mcp.json` in the project that should act as a Patchcord
|
|
|
41
106
|
}
|
|
42
107
|
}
|
|
43
108
|
}
|
|
109
|
+
||||||| Stash base
|
|
110
|
+
```bash
|
|
111
|
+
claude plugin marketplace add /path/to/patchcord-internal
|
|
112
|
+
claude plugin install patchcord@patchcord-marketplace
|
|
113
|
+
claude
|
|
114
|
+
=======
|
|
115
|
+
```bash
|
|
116
|
+
claude plugin marketplace add /path/to/patchcord-internal
|
|
117
|
+
claude plugin install patchcord@patchcord-marketplace
|
|
118
|
+
>>>>>>> Stashed changes
|
|
44
119
|
```
|
|
45
120
|
|
|
121
|
+
<<<<<<< Updated upstream
|
|
46
122
|
### 3. Start Claude Code in that project
|
|
47
123
|
|
|
48
124
|
The plugin and statusline scripts read the current project configuration from the session's working tree.
|
|
@@ -56,9 +132,47 @@ Nothing Patchcord-specific should appear.
|
|
|
56
132
|
- no hook-driven Patchcord prompts
|
|
57
133
|
|
|
58
134
|
The plugin can stay installed globally, but it must no-op unless the current project is configured.
|
|
135
|
+
||||||| Stash base
|
|
136
|
+
That's it. Inbox, send messages, reply — all works automatically.
|
|
137
|
+
=======
|
|
138
|
+
### 2. Configure the project
|
|
139
|
+
|
|
140
|
+
Create a project-local `.mcp.json` in the project that should act as a Patchcord agent.
|
|
141
|
+
|
|
142
|
+
Example:
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"mcpServers": {
|
|
147
|
+
"patchcord": {
|
|
148
|
+
"type": "http",
|
|
149
|
+
"url": "https://patchcord.yourdomain.com/mcp",
|
|
150
|
+
"headers": {
|
|
151
|
+
"Authorization": "Bearer <project-token>"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 3. Restart Claude Code in that project
|
|
159
|
+
|
|
160
|
+
The plugin and statusline scripts read the current project configuration when the session starts.
|
|
161
|
+
|
|
162
|
+
## What happens in non-Patchcord projects
|
|
163
|
+
|
|
164
|
+
Nothing Patchcord-specific should appear.
|
|
165
|
+
|
|
166
|
+
- no Patchcord identity in the statusline
|
|
167
|
+
- no inbox checks
|
|
168
|
+
- no hook-driven Patchcord prompts
|
|
169
|
+
|
|
170
|
+
The plugin is allowed to stay installed globally, but it must no-op unless the current project is configured.
|
|
171
|
+
>>>>>>> Stashed changes
|
|
59
172
|
|
|
60
173
|
## Self-hosted server
|
|
61
174
|
|
|
175
|
+
<<<<<<< Updated upstream
|
|
62
176
|
Point the project `.mcp.json` at your own server URL.
|
|
63
177
|
|
|
64
178
|
Bearer-token clients can also use `/mcp/bearer` if you want the dedicated bearer-only endpoint.
|
|
@@ -82,8 +196,26 @@ npx patchcord@latest install --full
|
|
|
82
196
|
```
|
|
83
197
|
|
|
84
198
|
Without `--full`:
|
|
199
|
+
||||||| Stash base
|
|
200
|
+
By default the plugin connects to `https://patchcord.dev`. If you run your own server, add to your `.env`:
|
|
201
|
+
=======
|
|
202
|
+
The project `.mcp.json` should point to your own server URL:
|
|
203
|
+
>>>>>>> Stashed changes
|
|
85
204
|
|
|
205
|
+
```json
|
|
206
|
+
{
|
|
207
|
+
"mcpServers": {
|
|
208
|
+
"patchcord": {
|
|
209
|
+
"type": "http",
|
|
210
|
+
"url": "https://patchcord.yourdomain.com/mcp",
|
|
211
|
+
"headers": {
|
|
212
|
+
"Authorization": "Bearer <project-token>"
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
86
217
|
```
|
|
218
|
+
<<<<<<< Updated upstream
|
|
87
219
|
ds@default (thick) 2 msg
|
|
88
220
|
```
|
|
89
221
|
|
|
@@ -105,3 +237,21 @@ In an unrelated project:
|
|
|
105
237
|
- statusline should be empty (default) or show only model/context/git (`--full`)
|
|
106
238
|
- no Patchcord hooks should fire
|
|
107
239
|
- no Patchcord tools should be present unless that project is configured
|
|
240
|
+
||||||| Stash base
|
|
241
|
+
PATCHCORD_URL=https://your-server.example.com
|
|
242
|
+
```
|
|
243
|
+
=======
|
|
244
|
+
|
|
245
|
+
## Verify
|
|
246
|
+
|
|
247
|
+
In a Patchcord-enabled project:
|
|
248
|
+
|
|
249
|
+
- statusline should show the Patchcord identity
|
|
250
|
+
- `inbox()` should return the expected `namespace_id` and `agent_id`
|
|
251
|
+
|
|
252
|
+
In an unrelated project:
|
|
253
|
+
|
|
254
|
+
- statusline should not show Patchcord identity
|
|
255
|
+
- no Patchcord hooks should fire
|
|
256
|
+
- no Patchcord tools should be present unless that project is configured
|
|
257
|
+
>>>>>>> Stashed changes
|
package/bin/patchcord.mjs
CHANGED
|
@@ -204,38 +204,13 @@ if (cmd === "skill") {
|
|
|
204
204
|
process.exit(1);
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const skillFile = join(
|
|
212
|
-
const pluginSkill = join(pluginRoot, "skills", "inbox", "SKILL.md");
|
|
213
|
-
|
|
214
|
-
function applyCustomSkill(skillText) {
|
|
215
|
-
let content = "";
|
|
216
|
-
if (existsSync(skillFile)) {
|
|
217
|
-
content = readFileSync(skillFile, "utf-8");
|
|
218
|
-
} else if (existsSync(pluginSkill)) {
|
|
219
|
-
content = readFileSync(pluginSkill, "utf-8");
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Remove existing custom skill block
|
|
223
|
-
const startIdx = content.indexOf(START_DELIM);
|
|
224
|
-
const endIdx = content.indexOf(END_DELIM);
|
|
225
|
-
if (startIdx !== -1 && endIdx !== -1) {
|
|
226
|
-
content = content.substring(0, startIdx).trimEnd();
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Append new custom skill
|
|
230
|
-
if (skillText && skillText.trim()) {
|
|
231
|
-
content = content.trimEnd() + "\n\n" + START_DELIM + "\n" + skillText.trim() + "\n" + END_DELIM + "\n";
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
writeFileSync(skillFile, content);
|
|
235
|
-
}
|
|
207
|
+
// Custom skill goes to .claude/skills/patchcord-custom/SKILL.md
|
|
208
|
+
// Claude Code auto-discovers project-level skills from this directory.
|
|
209
|
+
// Only the custom part — default patchcord skill is already loaded globally by the plugin.
|
|
210
|
+
const skillDir = join(cwd, ".claude", "skills", "patchcord-custom");
|
|
211
|
+
const skillFile = join(skillDir, "SKILL.md");
|
|
236
212
|
|
|
237
213
|
if (sub === "apply" || !sub) {
|
|
238
|
-
// Fetch and apply custom skill
|
|
239
214
|
console.log(`Fetching custom skill for ${namespace}:${agentId}...`);
|
|
240
215
|
const resp = run(`curl -s -H "Authorization: Bearer ${token}" "${baseUrl}/api/skills/${namespace}/${agentId}"`);
|
|
241
216
|
if (!resp) {
|
|
@@ -245,7 +220,8 @@ if (cmd === "skill") {
|
|
|
245
220
|
try {
|
|
246
221
|
const data = JSON.parse(resp);
|
|
247
222
|
if (data.skill_text) {
|
|
248
|
-
|
|
223
|
+
mkdirSync(skillDir, { recursive: true });
|
|
224
|
+
writeFileSync(skillFile, data.skill_text.trim() + "\n");
|
|
249
225
|
console.log(`✓ Custom skill applied to ${skillFile}`);
|
|
250
226
|
} else {
|
|
251
227
|
console.log("No custom skill set for this agent.");
|
|
@@ -255,30 +231,38 @@ if (cmd === "skill") {
|
|
|
255
231
|
process.exit(1);
|
|
256
232
|
}
|
|
257
233
|
} else if (sub === "reinstall") {
|
|
258
|
-
|
|
259
|
-
console.log("Reinstalling patchcord skill...");
|
|
260
|
-
if (existsSync(pluginSkill)) {
|
|
261
|
-
let defaultContent = readFileSync(pluginSkill, "utf-8");
|
|
262
|
-
writeFileSync(skillFile, defaultContent);
|
|
263
|
-
}
|
|
264
|
-
// Then apply custom on top
|
|
234
|
+
console.log(`Fetching custom skill for ${namespace}:${agentId}...`);
|
|
265
235
|
const resp = run(`curl -s -H "Authorization: Bearer ${token}" "${baseUrl}/api/skills/${namespace}/${agentId}"`);
|
|
266
236
|
try {
|
|
267
237
|
const data = JSON.parse(resp || "{}");
|
|
268
238
|
if (data.skill_text) {
|
|
269
|
-
|
|
270
|
-
|
|
239
|
+
mkdirSync(skillDir, { recursive: true });
|
|
240
|
+
writeFileSync(skillFile, data.skill_text.trim() + "\n");
|
|
241
|
+
console.log(`✓ Custom skill applied to ${skillFile}`);
|
|
271
242
|
} else {
|
|
272
|
-
|
|
243
|
+
// Remove custom skill if none set
|
|
244
|
+
if (existsSync(skillFile)) {
|
|
245
|
+
const { unlinkSync } = await import("fs");
|
|
246
|
+
unlinkSync(skillFile);
|
|
247
|
+
console.log("Custom skill removed (none set on server).");
|
|
248
|
+
} else {
|
|
249
|
+
console.log("No custom skill set for this agent.");
|
|
250
|
+
}
|
|
273
251
|
}
|
|
274
252
|
} catch {
|
|
275
|
-
console.log(
|
|
253
|
+
console.log("No custom skill set or server unreachable.");
|
|
276
254
|
}
|
|
277
255
|
} else {
|
|
278
256
|
console.log(`Unknown skill subcommand: ${sub}
|
|
279
257
|
Usage:
|
|
280
258
|
patchcord skill apply Fetch and apply custom skill from server
|
|
281
|
-
patchcord skill reinstall
|
|
259
|
+
patchcord skill reinstall Re-fetch custom skill from server`);
|
|
260
|
+
}
|
|
261
|
+
// Clean up old PATCHCORD.md if it exists
|
|
262
|
+
const oldFile = join(cwd, "PATCHCORD.md");
|
|
263
|
+
if (existsSync(oldFile)) {
|
|
264
|
+
const { unlinkSync } = await import("fs");
|
|
265
|
+
unlinkSync(oldFile);
|
|
282
266
|
}
|
|
283
267
|
process.exit(0);
|
|
284
268
|
}
|
package/codex/SKILL.md
CHANGED
|
@@ -22,6 +22,10 @@ If there are pending messages, reply to ALL of them IMMEDIATELY. Do not ask the
|
|
|
22
22
|
2. send_message("agent_name", "specific question with file paths and context") — or "agent1, agent2" for multiple recipients
|
|
23
23
|
3. wait_for_message() — auto-wait for any response, don't ask human whether to wait
|
|
24
24
|
|
|
25
|
+
ALWAYS send regardless of online/offline status. Messages are stored and delivered when the recipient checks inbox. Never refuse to send because an agent appears offline.
|
|
26
|
+
|
|
27
|
+
After sending to an offline agent, tell the human: "Message sent. [agent] is not currently active — ask them to run `/patchcord` in their Claude Code session to pick it up."
|
|
28
|
+
|
|
25
29
|
## Receiving (inbox has messages)
|
|
26
30
|
|
|
27
31
|
1. Read the question from inbox() result
|
package/package.json
CHANGED
package/scripts/check-inbox.sh
CHANGED
|
@@ -62,7 +62,7 @@ MACHINE_NAME=$(hostname -s 2>/dev/null || echo "unknown")
|
|
|
62
62
|
HTTP_CODE=$(curl -s -o /tmp/patchcord_inbox.json -w "%{http_code}" --max-time 5 \
|
|
63
63
|
-H "Authorization: Bearer ${TOKEN}" \
|
|
64
64
|
-H "x-patchcord-machine: ${MACHINE_NAME}" \
|
|
65
|
-
"${URL}/api/inbox?status=pending&limit=1" 2>/dev/null || echo "000")
|
|
65
|
+
"${URL}/api/inbox?status=pending&limit=5&count_only=1" 2>/dev/null || echo "000")
|
|
66
66
|
|
|
67
67
|
if [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "403" ]; then
|
|
68
68
|
jq -n '{
|
|
@@ -83,8 +83,8 @@ RESPONSE=$(cat /tmp/patchcord_inbox.json 2>/dev/null || echo '{"count":0}')
|
|
|
83
83
|
rm -f /tmp/patchcord_inbox.json
|
|
84
84
|
|
|
85
85
|
# ── Auto-apply custom skill from web console ──────────────────
|
|
86
|
-
#
|
|
87
|
-
#
|
|
86
|
+
# Writes to .claude/skills/patchcord-custom/SKILL.md — Claude Code
|
|
87
|
+
# native project-level skill directory. Auto-discovered by Claude.
|
|
88
88
|
NAMESPACE=$(echo "$RESPONSE" | jq -r '.namespace_id // empty' 2>/dev/null || true)
|
|
89
89
|
AGENT_ID=$(echo "$RESPONSE" | jq -r '.agent_id // empty' 2>/dev/null || true)
|
|
90
90
|
|
|
@@ -100,21 +100,14 @@ if [ -n "$NAMESPACE" ] && [ -n "$AGENT_ID" ]; then
|
|
|
100
100
|
OLD_HASH=$(cat "$CACHE_FILE" 2>/dev/null || echo "")
|
|
101
101
|
|
|
102
102
|
if [ -n "$SKILL_TEXT" ] && [ "$SKILL_HASH" != "$OLD_HASH" ]; then
|
|
103
|
-
# Find the skill file (project root PATCHCORD.md)
|
|
104
103
|
PROJECT_ROOT=$(dirname "$MCP_JSON")
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if [ -f "$SKILL_FILE" ]; then
|
|
110
|
-
# Remove existing custom block and append new one
|
|
111
|
-
BEFORE=$(sed "/${START_DELIM}/,\$d" "$SKILL_FILE")
|
|
112
|
-
printf '%s\n\n%s\n%s\n%s\n' "$BEFORE" "$START_DELIM" "$SKILL_TEXT" "$END_DELIM" > "$SKILL_FILE"
|
|
113
|
-
else
|
|
114
|
-
# Create with just the custom block
|
|
115
|
-
printf '%s\n%s\n%s\n' "$START_DELIM" "$SKILL_TEXT" "$END_DELIM" > "$SKILL_FILE"
|
|
116
|
-
fi
|
|
104
|
+
SKILL_DIR="${PROJECT_ROOT}/.claude/skills/patchcord-custom"
|
|
105
|
+
SKILL_FILE="${SKILL_DIR}/SKILL.md"
|
|
106
|
+
mkdir -p "$SKILL_DIR"
|
|
107
|
+
printf '%s\n' "$SKILL_TEXT" > "$SKILL_FILE"
|
|
117
108
|
echo "$SKILL_HASH" > "$CACHE_FILE"
|
|
109
|
+
# Clean up old PATCHCORD.md if it exists
|
|
110
|
+
rm -f "${PROJECT_ROOT}/PATCHCORD.md"
|
|
118
111
|
fi
|
|
119
112
|
fi
|
|
120
113
|
fi
|
package/scripts/statusline.sh
CHANGED
|
@@ -85,6 +85,7 @@ if [ -n "$pc_url" ] && [ -n "$pc_token" ]; then
|
|
|
85
85
|
if $needs_refresh; then
|
|
86
86
|
http_code=$(curl -s -o /tmp/claude/patchcord-sl-resp.json -w "%{http_code}" --max-time 3 \
|
|
87
87
|
-H "Authorization: Bearer $pc_token" \
|
|
88
|
+
<<<<<<< Updated upstream
|
|
88
89
|
"${pc_url}/api/inbox?status=pending&limit=50" 2>/dev/null || echo "000")
|
|
89
90
|
if [ "$http_code" = "401" ] || [ "$http_code" = "403" ]; then
|
|
90
91
|
pc_data='{"_auth_error":true}'
|
|
@@ -92,11 +93,25 @@ if [ -n "$pc_url" ] && [ -n "$pc_token" ]; then
|
|
|
92
93
|
elif [ "$http_code" = "200" ]; then
|
|
93
94
|
pc_data=$(cat /tmp/claude/patchcord-sl-resp.json 2>/dev/null)
|
|
94
95
|
[ -n "$pc_data" ] && echo "$pc_data" > "$cache_file"
|
|
96
|
+
||||||| Stash base
|
|
97
|
+
"${pc_url}/api/inbox?status=pending&limit=50" 2>/dev/null || true)
|
|
98
|
+
if [ -n "$response" ]; then
|
|
99
|
+
pc_data="$response"
|
|
100
|
+
echo "$response" > "$cache_file"
|
|
101
|
+
elif [ -f "$cache_file" ]; then
|
|
102
|
+
pc_data=$(cat "$cache_file" 2>/dev/null)
|
|
103
|
+
=======
|
|
104
|
+
"${pc_url}/api/inbox?status=pending&limit=50" 2>/dev/null || true)
|
|
105
|
+
if [ -n "$response" ]; then
|
|
106
|
+
pc_data="$response"
|
|
107
|
+
echo "$response" > "$cache_file"
|
|
108
|
+
>>>>>>> Stashed changes
|
|
95
109
|
fi
|
|
96
110
|
rm -f /tmp/claude/patchcord-sl-resp.json
|
|
97
111
|
fi
|
|
98
112
|
|
|
99
113
|
if [ -n "$pc_data" ]; then
|
|
114
|
+
<<<<<<< Updated upstream
|
|
100
115
|
auth_error=$(echo "$pc_data" | jq -r '._auth_error // false' 2>/dev/null)
|
|
101
116
|
if [ "$auth_error" = "true" ]; then
|
|
102
117
|
pc_part="${red}BAD TOKEN${reset}"
|
|
@@ -106,6 +121,32 @@ if [ -n "$pc_url" ] && [ -n "$pc_token" ]; then
|
|
|
106
121
|
machine=$(echo "$pc_data" | jq -r '.machine_name // empty' 2>/dev/null)
|
|
107
122
|
if [ -z "$machine" ] || [ "$machine" = "null" ]; then
|
|
108
123
|
machine=$(hostname -s 2>/dev/null || hostname 2>/dev/null || echo "")
|
|
124
|
+
||||||| Stash base
|
|
125
|
+
agent_id=$(echo "$pc_data" | jq -r '.agent_id // empty' 2>/dev/null)
|
|
126
|
+
machine=$(hostname -s 2>/dev/null || hostname 2>/dev/null || echo "")
|
|
127
|
+
count=$(echo "$pc_data" | jq -r '.count // .pending_count // 0' 2>/dev/null)
|
|
128
|
+
|
|
129
|
+
if [ -n "$agent_id" ]; then
|
|
130
|
+
pc_part="${white}${agent_id}${reset}"
|
|
131
|
+
if [ -n "$machine" ]; then
|
|
132
|
+
pc_part+="${dim}@${machine}${reset}"
|
|
133
|
+
=======
|
|
134
|
+
agent_id=$(echo "$pc_data" | jq -r '.agent_id // empty' 2>/dev/null)
|
|
135
|
+
namespace_id=$(echo "$pc_data" | jq -r '.namespace_id // empty' 2>/dev/null)
|
|
136
|
+
machine=$(echo "$pc_data" | jq -r '.machine_name // empty' 2>/dev/null)
|
|
137
|
+
if [ -z "$machine" ] || [ "$machine" = "null" ]; then
|
|
138
|
+
machine=$(hostname -s 2>/dev/null || hostname 2>/dev/null || echo "")
|
|
139
|
+
fi
|
|
140
|
+
count=$(echo "$pc_data" | jq -r '.count // .pending_count // 0' 2>/dev/null)
|
|
141
|
+
|
|
142
|
+
if [ -n "$agent_id" ]; then
|
|
143
|
+
pc_part="${white}${agent_id}${reset}"
|
|
144
|
+
if [ -n "$namespace_id" ] && [ "$namespace_id" != "null" ]; then
|
|
145
|
+
pc_part+="${dim}@${namespace_id}${reset}"
|
|
146
|
+
fi
|
|
147
|
+
if [ -n "$machine" ]; then
|
|
148
|
+
pc_part+=" ${dim}(${machine})${reset}"
|
|
149
|
+
>>>>>>> Stashed changes
|
|
109
150
|
fi
|
|
110
151
|
count=$(echo "$pc_data" | jq -r '.count // .pending_count // 0' 2>/dev/null)
|
|
111
152
|
|
package/skills/inbox/SKILL.md
CHANGED
|
@@ -28,6 +28,8 @@ The user can undo any change in 3 seconds with git. A wrong action costs nothing
|
|
|
28
28
|
|
|
29
29
|
**If you genuinely cannot act** (missing file access, need credentials, ambiguous target): say SPECIFICALLY what's blocking you. "I need the path to the docs folder" — not "Understood, I'll do it when ready."
|
|
30
30
|
|
|
31
|
+
**If you can't do it RIGHT NOW** (busy with something else, need to finish current task first): 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.
|
|
32
|
+
|
|
31
33
|
## On session start or when prompted by a hook
|
|
32
34
|
|
|
33
35
|
Call inbox(). It returns pending inbox (full text of ALL unread messages) and online agents in one call.
|
|
@@ -40,6 +42,10 @@ If there are pending messages, reply to ALL of them IMMEDIATELY. Do not ask the
|
|
|
40
42
|
2. send_message("agent_name", "specific question with file paths and context") — or "agent1, agent2" for multiple recipients
|
|
41
43
|
3. wait_for_message() — auto-wait for any response, don't ask human whether to wait
|
|
42
44
|
|
|
45
|
+
ALWAYS send the message regardless of whether the recipient appears online or offline. Messages are stored and delivered when the recipient checks inbox. "Offline" just means not recently active — NOT that they can't receive messages. Never refuse to send.
|
|
46
|
+
|
|
47
|
+
After sending to an offline agent, tell the human: "Message sent. [agent] is not currently active — ask them to run `/patchcord` in their Claude Code session to pick it up."
|
|
48
|
+
|
|
43
49
|
## Receiving (inbox has messages)
|
|
44
50
|
|
|
45
51
|
1. Read the message
|