patchcord 0.5.55 → 0.5.57
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 +48 -40
- package/package.json +1 -1
- package/skills/inbox/SKILL.md +15 -6
package/bin/patchcord.mjs
CHANGED
|
@@ -75,9 +75,7 @@ Usage:
|
|
|
75
75
|
patchcord --full Same + full statusline
|
|
76
76
|
patchcord --rename <new-name> [--agent-type <type>] Rename this agent
|
|
77
77
|
patchcord whoami Show your identity + project
|
|
78
|
-
patchcord whoami --propose "<text>"
|
|
79
|
-
patchcord whoami --approve <id> Approve a pending proposal
|
|
80
|
-
patchcord whoami --reject <id> Reject a pending proposal
|
|
78
|
+
patchcord whoami --propose "<text>" Update your whoami (two-shot: first call asks you to show human, second call applies)
|
|
81
79
|
patchcord agents List every agent (with whoami)
|
|
82
80
|
patchcord agents <name> Show one agent's whoami
|
|
83
81
|
patchcord subscribe Start the realtime listener
|
|
@@ -280,7 +278,8 @@ if (cmd === "whoami") {
|
|
|
280
278
|
process.exit(1);
|
|
281
279
|
}
|
|
282
280
|
|
|
283
|
-
// --propose "<text>"
|
|
281
|
+
// --propose "<text>" — two-shot gate. First call returns "show_human",
|
|
282
|
+
// second call with identical text returns "applied".
|
|
284
283
|
const proposeIdx = args.indexOf("--propose");
|
|
285
284
|
if (proposeIdx !== -1) {
|
|
286
285
|
const text = (args[proposeIdx + 1] || "").trim();
|
|
@@ -288,8 +287,9 @@ if (cmd === "whoami") {
|
|
|
288
287
|
console.error("Usage: patchcord whoami --propose \"<text>\"");
|
|
289
288
|
process.exit(1);
|
|
290
289
|
}
|
|
290
|
+
// Client-side length check — don't even round-trip if over limit.
|
|
291
291
|
if (text.length > 300) {
|
|
292
|
-
console.error(`
|
|
292
|
+
console.error(`whoami is ${text.length} characters; limit is 300. trim and retry.`);
|
|
293
293
|
process.exit(1);
|
|
294
294
|
}
|
|
295
295
|
const { status, json, body } = await _httpJSON("POST", `${baseUrl}/api/agent/whoami/propose`, token, { text });
|
|
@@ -297,35 +297,25 @@ if (cmd === "whoami") {
|
|
|
297
297
|
console.error(`✗ HTTP ${status}: ${(json && json.error) || body}`);
|
|
298
298
|
process.exit(1);
|
|
299
299
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// --approve <id> / --reject <id>
|
|
307
|
-
const approveIdx = args.indexOf("--approve");
|
|
308
|
-
const rejectIdx = args.indexOf("--reject");
|
|
309
|
-
if (approveIdx !== -1 || rejectIdx !== -1) {
|
|
310
|
-
const isApprove = approveIdx !== -1;
|
|
311
|
-
const idx = isApprove ? approveIdx : rejectIdx;
|
|
312
|
-
const id = (args[idx + 1] || "").trim();
|
|
313
|
-
if (!id) {
|
|
314
|
-
console.error(`Usage: patchcord whoami --${isApprove ? "approve" : "reject"} <proposal-id>`);
|
|
315
|
-
process.exit(1);
|
|
300
|
+
if (json.status === "applied") {
|
|
301
|
+
console.log(`✓ whoami applied.`);
|
|
302
|
+
console.log(`self: ${json.whoami}`);
|
|
303
|
+
process.exit(0);
|
|
316
304
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
{ proposal_id: id, action: isApprove ? "approve" : "reject" },
|
|
322
|
-
);
|
|
323
|
-
if (status !== "200") {
|
|
324
|
-
console.error(`✗ HTTP ${status}: ${(json && json.error) || body}`);
|
|
325
|
-
process.exit(1);
|
|
305
|
+
if (json.status === "unchanged") {
|
|
306
|
+
console.log(`unchanged — current whoami already matches.`);
|
|
307
|
+
console.log(`self: ${json.whoami}`);
|
|
308
|
+
process.exit(0);
|
|
326
309
|
}
|
|
327
|
-
|
|
328
|
-
|
|
310
|
+
if (json.status === "show_human") {
|
|
311
|
+
console.log(`gate: show the diff to the human, get explicit yes, then run the same command again to apply.`);
|
|
312
|
+
console.log();
|
|
313
|
+
console.log(`current: ${json.current || "(empty)"}`);
|
|
314
|
+
console.log(`proposed: ${json.proposed}`);
|
|
315
|
+
process.exit(0);
|
|
316
|
+
}
|
|
317
|
+
console.log(`unexpected response: ${body}`);
|
|
318
|
+
process.exit(1);
|
|
329
319
|
}
|
|
330
320
|
|
|
331
321
|
// plain whoami (no flags)
|
|
@@ -725,18 +715,32 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
|
|
|
725
715
|
globalCliInstalled = true;
|
|
726
716
|
} catch {
|
|
727
717
|
try {
|
|
728
|
-
const
|
|
729
|
-
mkdirSync(
|
|
730
|
-
cpSync(pluginRoot,
|
|
718
|
+
const fbStableDir = join(HOME, ".local", "share", "patchcord");
|
|
719
|
+
mkdirSync(fbStableDir, { recursive: true });
|
|
720
|
+
cpSync(pluginRoot, fbStableDir, { recursive: true, force: true });
|
|
731
721
|
const localBin = join(HOME, ".local", "bin");
|
|
732
722
|
mkdirSync(localBin, { recursive: true });
|
|
733
723
|
const wrapper = join(localBin, "patchcord");
|
|
734
|
-
writeFileSync(wrapper, `#!/bin/sh\nexec node "${join(
|
|
724
|
+
writeFileSync(wrapper, `#!/bin/sh\nexec node "${join(fbStableDir, "bin", "patchcord.mjs")}" "$@"\n`);
|
|
735
725
|
chmodSync(wrapper, 0o755);
|
|
736
726
|
globalCliInstalled = true;
|
|
737
727
|
} catch {}
|
|
738
728
|
}
|
|
739
729
|
|
|
730
|
+
// ── Stable copy for Claude Code plugin marketplace ──────────
|
|
731
|
+
// Claude Code's `plugin marketplace add` stores the literal directory
|
|
732
|
+
// path in its plugin manifest. If we point it at `pluginRoot` (the
|
|
733
|
+
// npx ephemeral cache), that directory gets wiped on the next `npx`
|
|
734
|
+
// invocation of any other package, and every Claude Code session
|
|
735
|
+
// afterward errors: "Plugin directory does not exist: ...".
|
|
736
|
+
// Maintain a stable mirror at ~/.local/share/patchcord/ and point
|
|
737
|
+
// the marketplace there instead.
|
|
738
|
+
const stablePluginDir = join(HOME, ".local", "share", "patchcord");
|
|
739
|
+
try {
|
|
740
|
+
mkdirSync(stablePluginDir, { recursive: true });
|
|
741
|
+
cpSync(pluginRoot, stablePluginDir, { recursive: true, force: true });
|
|
742
|
+
} catch {}
|
|
743
|
+
|
|
740
744
|
// ── Global setup (silent if nothing changed) ──
|
|
741
745
|
let globalChanges = [];
|
|
742
746
|
if (globalCliInstalled) globalChanges.push("Patchcord CLI installed globally");
|
|
@@ -750,10 +754,14 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
|
|
|
750
754
|
try { rmSync(npmCachePatchcord, { recursive: true, force: true }); } catch {}
|
|
751
755
|
}
|
|
752
756
|
|
|
753
|
-
// Always re-add marketplace (
|
|
754
|
-
//
|
|
755
|
-
//
|
|
756
|
-
|
|
757
|
+
// Always re-add marketplace (point at the STABLE mirror, not pluginRoot
|
|
758
|
+
// which is the npx ephemeral cache). Claude Code stores the literal
|
|
759
|
+
// path in its plugin manifest — pointing at npx cache breaks every
|
|
760
|
+
// future session as soon as that cache dir is swept.
|
|
761
|
+
// Remove any prior marketplace registration first so the path actually
|
|
762
|
+
// updates for users who got the buggy npx-cache path on an earlier install.
|
|
763
|
+
run(`claude plugin marketplace remove patchcord-marketplace`);
|
|
764
|
+
run(`claude plugin marketplace add "${stablePluginDir}"`);
|
|
757
765
|
const installed = run(`claude plugin list`)?.includes("patchcord");
|
|
758
766
|
wasPluginInstalled = !!installed;
|
|
759
767
|
if (installed) {
|
package/package.json
CHANGED
package/skills/inbox/SKILL.md
CHANGED
|
@@ -120,14 +120,23 @@ Send the returned `path` to the other agent in your message so they can download
|
|
|
120
120
|
- **Run `patchcord agents`** to see the full roster (every peer's whoami). One call, ~3KB, complete picture of the namespace.
|
|
121
121
|
- **Run `patchcord agents <name>`** when an unknown agent messages you and you want to know who they are before acting on their request.
|
|
122
122
|
|
|
123
|
-
### Updating your own whoami
|
|
123
|
+
### Updating your own whoami
|
|
124
124
|
|
|
125
|
-
300-char hard limit
|
|
125
|
+
300-char hard limit (CLI enforces client-side).
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
127
|
+
Server responds with one of three statuses:
|
|
128
|
+
|
|
129
|
+
- **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.
|
|
130
|
+
- **unchanged** — proposed text matches current. No-op.
|
|
131
|
+
- **show_human** — current value exists and the proposed text differs. Server printed `current:` and `proposed:`. You MUST:
|
|
132
|
+
1. Show the diff to the human in conversation
|
|
133
|
+
2. Ask them to confirm
|
|
134
|
+
3. Wait for explicit "yes"
|
|
135
|
+
4. Run the **exact same** `patchcord whoami --propose "<text>"` command again. Server will then return `applied`.
|
|
136
|
+
|
|
137
|
+
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.
|
|
138
|
+
|
|
139
|
+
Never call `--propose` a second time with the same text without showing the human between calls.
|
|
131
140
|
|
|
132
141
|
### Hard rules
|
|
133
142
|
|