patchcord 0.5.100 → 0.5.103
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
CHANGED
|
@@ -869,7 +869,7 @@ if (cmd === "main" || cmd === "master" || cmd === "provision" || cmd === "team")
|
|
|
869
869
|
const sub = process.argv[3];
|
|
870
870
|
if (sub === "connect") {
|
|
871
871
|
const harness = flagVal("harness", flagVal("tool", ""));
|
|
872
|
-
const create = run(`curl -sf --max-time 10 -X POST "${DEFAULT_API}/api/
|
|
872
|
+
const create = run(`curl -sf --max-time 10 -X POST "${DEFAULT_API}/api/main/session" -H "Content-Type: application/json" -d '{"harness":"${harness}"}'`);
|
|
873
873
|
let sessionId = "";
|
|
874
874
|
try { sessionId = (JSON.parse(create).session_id) || ""; } catch {}
|
|
875
875
|
if (!sessionId) { console.error("Could not start main-auth session."); process.exit(1); }
|
|
@@ -904,7 +904,7 @@ if (cmd === "main" || cmd === "master" || cmd === "provision" || cmd === "team")
|
|
|
904
904
|
}
|
|
905
905
|
if (sub === "whoami") {
|
|
906
906
|
const m = requireMain();
|
|
907
|
-
const { status, json } = await _httpJSON("GET", `${m.baseUrl}/api/
|
|
907
|
+
const { status, json } = await _httpJSON("GET", `${m.baseUrl}/api/main/whoami`, m.token);
|
|
908
908
|
if (status !== "200" || !json) { console.error(`main whoami failed (HTTP ${status})`); process.exit(1); }
|
|
909
909
|
console.log(`main · user ${json.user_id} · ${json.agents}/${json.quota} agents`);
|
|
910
910
|
process.exit(0);
|
|
@@ -1458,22 +1458,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
|
|
|
1458
1458
|
// Kimi CLI
|
|
1459
1459
|
const hasKimi = run("which kimi");
|
|
1460
1460
|
if (hasKimi) {
|
|
1461
|
-
// Clean up old skill names (hyphen-style combined skill)
|
|
1462
|
-
const oldKimiSkillDir = join(HOME, ".kimi", "skills", "patchcord");
|
|
1463
|
-
const oldKimiWaitDir = join(HOME, ".kimi", "skills", "patchcord-wait");
|
|
1464
|
-
if (existsSync(oldKimiSkillDir)) {
|
|
1465
|
-
rmSync(oldKimiSkillDir, { recursive: true, force: true });
|
|
1466
|
-
}
|
|
1467
|
-
if (existsSync(oldKimiWaitDir)) {
|
|
1468
|
-
rmSync(oldKimiWaitDir, { recursive: true, force: true });
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
|
-
// Install three focused Kimi skills
|
|
1472
|
-
const kimiInboxDir = join(HOME, ".kimi", "skills", "patchcord:inbox");
|
|
1473
|
-
const kimiWaitDir = join(HOME, ".kimi", "skills", "patchcord:wait");
|
|
1474
|
-
const kimiSubDir = join(HOME, ".kimi", "skills", "patchcord:subscribe");
|
|
1475
1461
|
let kimiChanged = false;
|
|
1476
|
-
|
|
1477
1462
|
const installKimiSkill = (destDir, relSrc) => {
|
|
1478
1463
|
const src = join(pluginRoot, "per-project-skills", "kimi", relSrc, "SKILL.md");
|
|
1479
1464
|
const dest = join(destDir, "SKILL.md");
|
|
@@ -1482,9 +1467,24 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
|
|
|
1482
1467
|
copyFileSync(src, dest);
|
|
1483
1468
|
return changed;
|
|
1484
1469
|
};
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1470
|
+
// Kimi CLI (Python) reads ~/.kimi/skills; Kimi Code (Node) reads
|
|
1471
|
+
// ~/.kimi-code/skills. Install into BOTH user dirs that exist — a global
|
|
1472
|
+
// `npx patchcord` must fix whichever Kimi the user runs. Clean up the old
|
|
1473
|
+
// combined skill AND colon-named flow skills (Kimi requires names to be
|
|
1474
|
+
// lowercase/digits/hyphens only — a colon kept them out of /flow:, showing
|
|
1475
|
+
// them only under /skill:). Renamed to patchcord-inbox etc.
|
|
1476
|
+
const kimiCodeHome = process.env.KIMI_CODE_HOME || join(HOME, ".kimi-code");
|
|
1477
|
+
const kimiSkillRoots = [join(HOME, ".kimi", "skills")];
|
|
1478
|
+
if (existsSync(kimiCodeHome)) kimiSkillRoots.push(join(kimiCodeHome, "skills"));
|
|
1479
|
+
for (const root of kimiSkillRoots) {
|
|
1480
|
+
for (const stale of ["patchcord", "patchcord-wait", "patchcord:inbox", "patchcord:wait", "patchcord:subscribe"]) {
|
|
1481
|
+
const d = join(root, stale);
|
|
1482
|
+
if (existsSync(d)) { try { rmSync(d, { recursive: true, force: true }); } catch {} }
|
|
1483
|
+
}
|
|
1484
|
+
kimiChanged = installKimiSkill(join(root, "patchcord-inbox"), "inbox") || kimiChanged;
|
|
1485
|
+
kimiChanged = installKimiSkill(join(root, "patchcord-wait"), "wait") || kimiChanged;
|
|
1486
|
+
kimiChanged = installKimiSkill(join(root, "patchcord-subscribe"), "subscribe") || kimiChanged;
|
|
1487
|
+
}
|
|
1488
1488
|
|
|
1489
1489
|
// Install/update stop hook — fires after each Kimi turn to check inbox
|
|
1490
1490
|
let hookChanged = false;
|
|
@@ -2333,14 +2333,20 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
|
|
|
2333
2333
|
console.log(`\n ${green}✓${r} Kimi Code configured: ${dim}${kcPath}${r}`);
|
|
2334
2334
|
console.log(` ${dim}Kimi Code auto-loads project-local .kimi-code/mcp.json — just run ${r}${bold}kimi${r}${dim} in this project (no wrapper needed).${r}`);
|
|
2335
2335
|
}
|
|
2336
|
-
// Skills → user-level Kimi Code skills dir
|
|
2336
|
+
// Skills → user-level Kimi Code skills dir. Hyphen names so they register
|
|
2337
|
+
// as /flow:patchcord-inbox (a colon kept them out of /flow:). Remove any
|
|
2338
|
+
// stale colon-named dirs from prior installs.
|
|
2337
2339
|
try {
|
|
2338
|
-
for (const
|
|
2340
|
+
for (const stale of ["patchcord:inbox", "patchcord:wait", "patchcord:subscribe", "patchcord"]) {
|
|
2341
|
+
const d = join(kimiCodeHome, "skills", stale);
|
|
2342
|
+
if (existsSync(d)) { try { rmSync(d, { recursive: true, force: true }); } catch {} }
|
|
2343
|
+
}
|
|
2344
|
+
for (const [label, rel] of [["patchcord-inbox", "inbox"], ["patchcord-wait", "wait"], ["patchcord-subscribe", "subscribe"]]) {
|
|
2339
2345
|
const dest = join(kimiCodeHome, "skills", label);
|
|
2340
2346
|
mkdirSync(dest, { recursive: true });
|
|
2341
2347
|
cpSync(join(pluginRoot, "per-project-skills", "kimi", rel, "SKILL.md"), join(dest, "SKILL.md"));
|
|
2342
2348
|
}
|
|
2343
|
-
console.log(` ${green}✓${r} Skills installed: ${dim}${join(kimiCodeHome, "skills")}${r}`);
|
|
2349
|
+
console.log(` ${green}✓${r} Skills installed: ${dim}${join(kimiCodeHome, "skills")}${r} ${dim}(/flow:patchcord-inbox)${r}`);
|
|
2344
2350
|
} catch {}
|
|
2345
2351
|
} else if (isKimi) {
|
|
2346
2352
|
// Kimi CLI (Python): per-project .kimi/mcp.json + shell wrapper for --mcp-config-file
|
|
@@ -2360,22 +2366,22 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
|
|
|
2360
2366
|
console.log(`\n ${green}✓${r} Kimi CLI configured: ${dim}${kimiPath}${r}`);
|
|
2361
2367
|
console.log(` ${dim}Per-project — use the kimi-pc wrapper (see below) so Kimi loads this config.${r}`);
|
|
2362
2368
|
}
|
|
2363
|
-
// Install/update global skills
|
|
2364
|
-
|
|
2365
|
-
const
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
const kimiInboxDir = join(HOME, ".kimi", "skills", "patchcord
|
|
2370
|
-
const kimiWaitDir = join(HOME, ".kimi", "skills", "patchcord
|
|
2371
|
-
const kimiSubDir = join(HOME, ".kimi", "skills", "patchcord
|
|
2369
|
+
// Install/update global skills. Hyphen names → /flow:patchcord-inbox
|
|
2370
|
+
// (colon names never registered as flows). Remove old combined + colon dirs.
|
|
2371
|
+
for (const stale of ["patchcord", "patchcord-wait", "patchcord:inbox", "patchcord:wait", "patchcord:subscribe"]) {
|
|
2372
|
+
const d = join(HOME, ".kimi", "skills", stale);
|
|
2373
|
+
if (existsSync(d)) { try { rmSync(d, { recursive: true, force: true }); } catch {} }
|
|
2374
|
+
}
|
|
2375
|
+
const kimiInboxDir = join(HOME, ".kimi", "skills", "patchcord-inbox");
|
|
2376
|
+
const kimiWaitDir = join(HOME, ".kimi", "skills", "patchcord-wait");
|
|
2377
|
+
const kimiSubDir = join(HOME, ".kimi", "skills", "patchcord-subscribe");
|
|
2372
2378
|
mkdirSync(kimiInboxDir, { recursive: true });
|
|
2373
2379
|
mkdirSync(kimiWaitDir, { recursive: true });
|
|
2374
2380
|
mkdirSync(kimiSubDir, { recursive: true });
|
|
2375
2381
|
cpSync(join(pluginRoot, "per-project-skills", "kimi", "inbox", "SKILL.md"), join(kimiInboxDir, "SKILL.md"));
|
|
2376
2382
|
cpSync(join(pluginRoot, "per-project-skills", "kimi", "wait", "SKILL.md"), join(kimiWaitDir, "SKILL.md"));
|
|
2377
2383
|
cpSync(join(pluginRoot, "per-project-skills", "kimi", "subscribe", "SKILL.md"), join(kimiSubDir, "SKILL.md"));
|
|
2378
|
-
console.log(` ${green}✓${r} Skills installed: ${dim}patchcord
|
|
2384
|
+
console.log(` ${green}✓${r} Skills installed: ${dim}/flow:patchcord-inbox${r}, ${dim}-wait${r}, ${dim}-subscribe${r}`);
|
|
2379
2385
|
|
|
2380
2386
|
// Install alias for per-project --mcp-config-file
|
|
2381
2387
|
const aliasLine = `alias kimi-pc='kimi --mcp-config-file .kimi/mcp.json'`;
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: patchcord
|
|
2
|
+
name: patchcord-inbox
|
|
3
3
|
description: Read Patchcord inbox and reply to messages
|
|
4
4
|
type: flow
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
> Use `/flow:patchcord
|
|
7
|
+
> Use `/flow:patchcord-inbox` to run this. `/skill:` only loads it as docs (Kimi design).
|
|
8
8
|
|
|
9
9
|
```mermaid
|
|
10
10
|
flowchart TD
|
|
11
|
-
A([BEGIN]) --> B[Call the inbox MCP tool. Reading the response: the first header line shows YOUR own identity (the recipient). The actual sender of each message is
|
|
12
|
-
B --> C[Call the Shell tool with these exact parameters to re-arm the listener: command=
|
|
11
|
+
A([BEGIN]) --> B["Call the inbox MCP tool. Reading the response: the first header line shows YOUR own identity (the recipient). The actual sender of each message is on a 'From X' line — never confuse the header with the sender. For each pending message, classify it: (1) ACK — short signals like thanks, noted, works, great, ok, 👍 with no task. Close silently with reply(message_id, resolve=true) and NO content. Never send a text reply to an ack — it creates infinite ack chains. (2) BLOCKED — you cannot do the work right now (busy, missing credentials, ambiguous target). Use reply(message_id, reason, defer=true) — the message stays in your inbox as a reminder. Never silently skip a message. (3) ACTIONABLE — do the work the message asks for first (edit the file, run the command, write the code), THEN call reply(message_id, concrete summary) with file paths and line numbers. Never reply will-do or understood before doing the work."]
|
|
12
|
+
B --> C["Call the Shell tool with these exact parameters to re-arm the listener: command=patchcord subscribe --kimi 5, run_in_background=true, timeout=86400, description=Patchcord listener."]
|
|
13
13
|
C --> D([END])
|
|
14
14
|
```
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: patchcord
|
|
2
|
+
name: patchcord-subscribe
|
|
3
3
|
description: Start the Patchcord background polling listener
|
|
4
4
|
type: flow
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
> Use `/flow:patchcord
|
|
7
|
+
> Use `/flow:patchcord-subscribe` to run this. `/skill:` only loads it as docs (Kimi design).
|
|
8
8
|
|
|
9
9
|
```mermaid
|
|
10
10
|
flowchart TD
|
|
11
|
-
A([BEGIN]) --> B[First, call the inbox MCP tool. Reading the response: the first header line shows YOUR own identity (the recipient). The actual sender of each message is
|
|
12
|
-
B --> C[Now call the Shell tool with these exact parameters: command=
|
|
11
|
+
A([BEGIN]) --> B["First, call the inbox MCP tool. Reading the response: the first header line shows YOUR own identity (the recipient). The actual sender of each message is on a 'From X' line — never confuse the header with the sender. Drain each pending message by classifying it: ACK (thanks, noted, works, ok, 👍) → close silently with reply(message_id, resolve=true), no content; BLOCKED (you cannot do the work right now) → reply(message_id, reason, defer=true); ACTIONABLE → do the work first, then reply(message_id, concrete summary). Do NOT skip the drain — the listener exits immediately if messages are already waiting."]
|
|
12
|
+
B --> C["Now call the Shell tool with these exact parameters: command=patchcord subscribe --kimi 5, run_in_background=true, timeout=86400, description=Patchcord listener. Then tell the user in one sentence: Patchcord listener active."]
|
|
13
13
|
C --> D([END])
|
|
14
14
|
```
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: patchcord
|
|
2
|
+
name: patchcord-wait
|
|
3
3
|
description: Wait for one incoming Patchcord message
|
|
4
4
|
type: flow
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
> Use `/flow:patchcord
|
|
7
|
+
> Use `/flow:patchcord-wait` to run this. `/skill:` only loads it as docs (Kimi design).
|
|
8
8
|
|
|
9
9
|
```mermaid
|
|
10
10
|
flowchart TD
|
|
11
|
-
A([BEGIN]) --> B[Call the wait_for_message MCP tool to block until a message arrives or 5 minutes elapse.]
|
|
12
|
-
B --> C{Message arrived?}
|
|
13
|
-
C -->|Yes| D[Classify the message: ACK (
|
|
11
|
+
A([BEGIN]) --> B["Call the wait_for_message MCP tool to block until a message arrives or 5 minutes elapse."]
|
|
12
|
+
B --> C{"Message arrived?"}
|
|
13
|
+
C -->|Yes| D["Classify the message: ACK (thanks, noted, works, ok, 👍) → close silently with reply(message_id, resolve=true), no content; BLOCKED (cannot do work right now) → reply(message_id, reason, defer=true); ACTIONABLE → do the work first, then reply(message_id, concrete summary). Never reply to an ack with text."]
|
|
14
14
|
C -->|No| E([END])
|
|
15
|
-
D --> F[Call the Shell tool with these exact parameters to re-arm the listener: command=
|
|
15
|
+
D --> F["Call the Shell tool with these exact parameters to re-arm the listener: command=patchcord subscribe --kimi 5, run_in_background=true, timeout=86400, description=Patchcord listener."]
|
|
16
16
|
F --> E
|
|
17
17
|
```
|