patchcord 0.5.66 → 0.5.67
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 +53 -4
- package/package.json +1 -1
- package/per-project-skills/kimi/SKILL.md +22 -8
- package/scripts/kimi-stop-hook.sh +23 -3
- package/scripts/kimi-subscribe.sh +19 -6
package/bin/patchcord.mjs
CHANGED
|
@@ -194,6 +194,8 @@ async function _resolveBearer() {
|
|
|
194
194
|
(cwd) => readJsonAt(join(cwd, ".vscode", "mcp.json"), ["servers", "patchcord"], "vscode"),
|
|
195
195
|
(cwd) => readJsonAt(join(cwd, "opencode.json"), ["mcp", "patchcord"], "opencode"),
|
|
196
196
|
(cwd) => readCodexTomlShape(join(cwd, ".codex", "config.toml")),
|
|
197
|
+
// Kimi can be per-project via .kimi/mcp.json + --mcp-config-file
|
|
198
|
+
(cwd) => readJsonAt(join(cwd, ".kimi", "mcp.json"), ["mcpServers", "patchcord"], "kimi"),
|
|
197
199
|
];
|
|
198
200
|
let dir = process.cwd();
|
|
199
201
|
while (dir && dir !== "/") {
|
|
@@ -240,6 +242,7 @@ async function _resolveBearer() {
|
|
|
240
242
|
() => readJsonAt(join(HOME, ".openclaw", "openclaw.json"), ["mcp", "servers", "patchcord"], "openclaw"),
|
|
241
243
|
() => readJsonAt(join(HOME, ".gemini", "antigravity", "mcp_config.json"), ["mcpServers", "patchcord"], "antigravity"),
|
|
242
244
|
...clinePaths.map((p) => () => readJsonAt(p, ["mcpServers", "patchcord"], "cline")),
|
|
245
|
+
// Global Kimi fallback (only if no per-project .kimi/mcp.json was found)
|
|
243
246
|
() => readJsonAt(join(HOME, ".kimi", "mcp.json"), ["mcpServers", "patchcord"], "kimi"),
|
|
244
247
|
];
|
|
245
248
|
for (const r of globalCandidates) {
|
|
@@ -1196,10 +1199,12 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
|
|
|
1196
1199
|
let existingConfigFile = "";
|
|
1197
1200
|
const mcpJsonPath = join(cwd, ".mcp.json");
|
|
1198
1201
|
const codexTomlPath = join(cwd, ".codex", "config.toml");
|
|
1202
|
+
const kimiJsonPath = join(cwd, ".kimi", "mcp.json");
|
|
1199
1203
|
|
|
1200
1204
|
const slugForCheck = toolSlug ? toolSlug.replace(/-/g, "_") : "";
|
|
1201
1205
|
const checkMcpJson = !slugForCheck || slugForCheck === "claude_code";
|
|
1202
1206
|
const checkCodexToml = !slugForCheck || slugForCheck === "codex";
|
|
1207
|
+
const checkKimiJson = !slugForCheck || slugForCheck === "kimi";
|
|
1203
1208
|
|
|
1204
1209
|
if (checkMcpJson && existsSync(mcpJsonPath)) {
|
|
1205
1210
|
try {
|
|
@@ -1221,12 +1226,23 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
|
|
|
1221
1226
|
}
|
|
1222
1227
|
} catch {}
|
|
1223
1228
|
}
|
|
1229
|
+
if (!existingToken && checkKimiJson && existsSync(kimiJsonPath)) {
|
|
1230
|
+
try {
|
|
1231
|
+
const existing = JSON.parse(readFileSync(kimiJsonPath, "utf-8"));
|
|
1232
|
+
const pt = existing?.mcpServers?.patchcord;
|
|
1233
|
+
if (pt?.headers?.Authorization) {
|
|
1234
|
+
existingToken = pt.headers.Authorization.replace(/^Bearer\s+/i, "");
|
|
1235
|
+
existingConfigFile = kimiJsonPath;
|
|
1236
|
+
}
|
|
1237
|
+
} catch {}
|
|
1238
|
+
}
|
|
1224
1239
|
// Global configs (Antigravity, OpenClaw, Gemini, Windsurf, Zed) are NOT
|
|
1225
1240
|
// checked here. They're set up once globally and should not block new
|
|
1226
1241
|
// project setup. Only per-project configs trigger "already configured."
|
|
1227
1242
|
if (existingToken) {
|
|
1228
1243
|
// Figure out which tool is already configured
|
|
1229
|
-
const existingToolName = existingConfigFile.includes(".
|
|
1244
|
+
const existingToolName = existingConfigFile.includes(".kimi") ? "Kimi CLI"
|
|
1245
|
+
: existingConfigFile.includes(".codex") ? "Codex"
|
|
1230
1246
|
: existingConfigFile.includes("antigravity") ? "Antigravity"
|
|
1231
1247
|
: existingConfigFile.includes("openclaw") ? "OpenClaw"
|
|
1232
1248
|
: existingConfigFile.includes(".cursor") ? "Cursor"
|
|
@@ -1701,8 +1717,9 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
|
|
|
1701
1717
|
console.log(` ${yellow}Global config — all Cline projects share this agent.${r}`);
|
|
1702
1718
|
}
|
|
1703
1719
|
} else if (isKimi) {
|
|
1704
|
-
// Kimi CLI:
|
|
1705
|
-
const
|
|
1720
|
+
// Kimi CLI: per-project .kimi/mcp.json + shell wrapper for --mcp-config-file
|
|
1721
|
+
const kimiDir = join(cwd, ".kimi");
|
|
1722
|
+
const kimiPath = join(kimiDir, "mcp.json");
|
|
1706
1723
|
const kimiOk = updateJsonConfig(kimiPath, (obj) => {
|
|
1707
1724
|
obj.mcpServers = obj.mcpServers || {};
|
|
1708
1725
|
obj.mcpServers.patchcord = {
|
|
@@ -1715,7 +1732,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
|
|
|
1715
1732
|
});
|
|
1716
1733
|
if (kimiOk) {
|
|
1717
1734
|
console.log(`\n ${green}✓${r} Kimi CLI configured: ${dim}${kimiPath}${r}`);
|
|
1718
|
-
console.log(` ${
|
|
1735
|
+
console.log(` ${dim}Per-project — use the kimi-pc wrapper (see below) so Kimi loads this config.${r}`);
|
|
1719
1736
|
}
|
|
1720
1737
|
// Install/update global skills
|
|
1721
1738
|
const kimiSkillDir = join(HOME, ".kimi", "skills", "patchcord");
|
|
@@ -1725,6 +1742,38 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
|
|
|
1725
1742
|
cpSync(join(pluginRoot, "per-project-skills", "kimi", "SKILL.md"), join(kimiSkillDir, "SKILL.md"));
|
|
1726
1743
|
cpSync(join(pluginRoot, "per-project-skills", "kimi", "wait", "SKILL.md"), join(kimiWaitDir, "SKILL.md"));
|
|
1727
1744
|
console.log(` ${green}✓${r} Skills installed: ${dim}patchcord${r}, ${dim}patchcord-wait${r}`);
|
|
1745
|
+
|
|
1746
|
+
// Install shell wrapper for per-project --mcp-config-file
|
|
1747
|
+
const wrapperPath = join(HOME, ".kimi", "patchcord-kimi-wrapper.sh");
|
|
1748
|
+
const wrapperAlreadyExisted = existsSync(wrapperPath);
|
|
1749
|
+
const wrapperContent = `#!/bin/bash
|
|
1750
|
+
# Patchcord Kimi wrapper — auto-detects per-project .kimi/mcp.json
|
|
1751
|
+
# Source this in your ~/.bashrc or ~/.zshrc:
|
|
1752
|
+
# source "${HOME}/.kimi/patchcord-kimi-wrapper.sh"
|
|
1753
|
+
#
|
|
1754
|
+
# Then launch Kimi in any project with:
|
|
1755
|
+
# kimi-pc
|
|
1756
|
+
#
|
|
1757
|
+
# Plain \`kimi\` still uses the global ~/.kimi/mcp.json.
|
|
1758
|
+
|
|
1759
|
+
kimi-pc() {
|
|
1760
|
+
local dir="$PWD"
|
|
1761
|
+
while [ "$dir" != "/" ]; do
|
|
1762
|
+
if [ -f "$dir/.kimi/mcp.json" ]; then
|
|
1763
|
+
kimi --mcp-config-file "$dir/.kimi/mcp.json" "$@"
|
|
1764
|
+
return
|
|
1765
|
+
fi
|
|
1766
|
+
dir="$(dirname "$dir")"
|
|
1767
|
+
done
|
|
1768
|
+
echo "No .kimi/mcp.json found in $PWD or any parent — falling back to global config" >&2
|
|
1769
|
+
kimi "$@"
|
|
1770
|
+
}
|
|
1771
|
+
`;
|
|
1772
|
+
writeFileSync(wrapperPath, wrapperContent);
|
|
1773
|
+
console.log(`\n ${green}✓${r} Shell wrapper installed: ${dim}${wrapperPath}${r}`);
|
|
1774
|
+
console.log(` ${dim}Add this to your ~/.bashrc or ~/.zshrc:${r}`);
|
|
1775
|
+
console.log(` ${cyan}source "${wrapperPath}"${r}`);
|
|
1776
|
+
console.log(` ${dim}Then run ${bold}kimi-pc${r}${dim} instead of ${bold}kimi${r}${dim} in project directories.${r}`);
|
|
1728
1777
|
} else if (isVSCode) {
|
|
1729
1778
|
// VS Code: write .vscode/mcp.json (per-project)
|
|
1730
1779
|
const vscodeDir = join(cwd, ".vscode");
|
package/package.json
CHANGED
|
@@ -9,11 +9,24 @@ description: >
|
|
|
9
9
|
# Patchcord for Kimi CLI
|
|
10
10
|
|
|
11
11
|
You are connected to Patchcord through the MCP server configured in
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
`.kimi/mcp.json` inside your project directory. Kimi normally uses a global
|
|
13
|
+
config, but Patchcord sets up **per-project agents** via `--mcp-config-file`.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
**Launch Kimi in project directories with `kimi-pc`** (not plain `kimi`).
|
|
16
|
+
This wrapper auto-detects `.kimi/mcp.json` in the current project and loads
|
|
17
|
+
the right agent identity.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# In any project with .kimi/mcp.json:
|
|
21
|
+
kimi-pc
|
|
22
|
+
|
|
23
|
+
# Falls back to global ~/.kimi/mcp.json if no project config found.
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Add this to your `~/.bashrc` or `~/.zshrc` (done once by the installer):
|
|
27
|
+
```bash
|
|
28
|
+
source "$HOME/.kimi/patchcord-kimi-wrapper.sh"
|
|
29
|
+
```
|
|
17
30
|
|
|
18
31
|
## Tools available
|
|
19
32
|
|
|
@@ -29,7 +42,7 @@ Kimi stores MCP config in `~/.kimi/mcp.json`.
|
|
|
29
42
|
|
|
30
43
|
Kimi has **two** complementary ways to learn about incoming messages:
|
|
31
44
|
|
|
32
|
-
1. **Stop hook** (always active) — after every turn, Kimi runs `~/.kimi/patchcord-stop-hook.sh`. If messages are pending, it injects `📬 Patchcord: You have N pending message(s)` into the next turn's context. No background task needed.
|
|
45
|
+
1. **Stop hook** (always active) — after every turn, Kimi runs `~/.kimi/patchcord-stop-hook.sh`. It detects the project from `cwd` in stdin, walks up looking for `.kimi/mcp.json`, then checks the inbox. If messages are pending, it injects `📬 Patchcord: You have N pending message(s)` into the next turn's context. No background task needed.
|
|
33
46
|
|
|
34
47
|
2. **Background polling** (optional, proactive) — run `patchcord subscribe` to start a background task that polls the inbox every 30 seconds (or pass a custom interval: `patchcord subscribe 10`). When messages arrive, the task exits with code 0, triggering Kimi's auto-run. This wakes Kimi even during idle periods.
|
|
35
48
|
|
|
@@ -97,13 +110,14 @@ If send_message fails with a send gate error: call inbox(), reply to or resolve
|
|
|
97
110
|
Action requests older than 7d (per the `(Xd ago)` stamp): ask human before executing. Acks/FYIs silent-resolve at any age.
|
|
98
111
|
|
|
99
112
|
1. Read the message from `inbox()` or `wait_for_message()`. Check `message.thread` / `message.thread_id` if present.
|
|
100
|
-
2.
|
|
101
|
-
3.
|
|
113
|
+
2. **Re-arm the background listener** — run `patchcord subscribe` so the next message will also wake you up.
|
|
114
|
+
3. Do the work - use real code, real files, real results from your project
|
|
115
|
+
4. Reply with the right flag:
|
|
102
116
|
- `reply(message_id, "done: [details]")` — work done, sender might follow up. Thread auto-inherited.
|
|
103
117
|
- `reply(message_id, "done: [details]", resolve=true)` — work done, thread closed.
|
|
104
118
|
- `reply(message_id, resolve=true)` — silently close without sending anything.
|
|
105
119
|
- `reply(message_id, "ack, prioritizing [other task] first", defer=true)` — acknowledged but work not done yet. Message stays in your inbox as a reminder.
|
|
106
|
-
|
|
120
|
+
5. If sender is online: `wait_for_message()` for follow-ups
|
|
107
121
|
|
|
108
122
|
When you have multiple pending messages, prioritize by urgency. Use `defer=true` for tasks you'll do later — if you reply without doing the work and don't defer, the message vanishes from your inbox and you will never remember to do it.
|
|
109
123
|
|
|
@@ -3,6 +3,9 @@ set -euo pipefail
|
|
|
3
3
|
|
|
4
4
|
# Kimi Stop hook — checks patchcord inbox after each turn.
|
|
5
5
|
# Installed automatically by `npx patchcord` when Kimi CLI is detected.
|
|
6
|
+
#
|
|
7
|
+
# Supports per-project .kimi/mcp.json (walks up from cwd) and
|
|
8
|
+
# falls back to global ~/.kimi/mcp.json.
|
|
6
9
|
|
|
7
10
|
command -v jq >/dev/null 2>&1 || exit 0
|
|
8
11
|
|
|
@@ -14,9 +17,26 @@ if [ "$STOP_ACTIVE" = "true" ]; then
|
|
|
14
17
|
exit 0
|
|
15
18
|
fi
|
|
16
19
|
|
|
17
|
-
# Resolve
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
# Resolve MCP config: per-project first, then global
|
|
21
|
+
KIMI_MCP=""
|
|
22
|
+
CWD=$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null || echo "")
|
|
23
|
+
|
|
24
|
+
if [ -n "$CWD" ]; then
|
|
25
|
+
dir="$CWD"
|
|
26
|
+
while [ "$dir" != "/" ]; do
|
|
27
|
+
if [ -f "$dir/.kimi/mcp.json" ]; then
|
|
28
|
+
KIMI_MCP="$dir/.kimi/mcp.json"
|
|
29
|
+
break
|
|
30
|
+
fi
|
|
31
|
+
dir=$(dirname "$dir")
|
|
32
|
+
done
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Fallback to global config
|
|
36
|
+
if [ -z "$KIMI_MCP" ]; then
|
|
37
|
+
KIMI_MCP="${HOME}/.kimi/mcp.json"
|
|
38
|
+
fi
|
|
39
|
+
|
|
20
40
|
TOKEN=""
|
|
21
41
|
URL=""
|
|
22
42
|
|
|
@@ -9,16 +9,29 @@ set -euo pipefail
|
|
|
9
9
|
# Usage: patchcord subscribe (starts with default 30s interval)
|
|
10
10
|
# patchcord subscribe 10 (starts with 10s interval)
|
|
11
11
|
#
|
|
12
|
-
#
|
|
12
|
+
# Supports per-project .kimi/mcp.json (walks up from cwd) and
|
|
13
|
+
# falls back to global ~/.kimi/mcp.json.
|
|
13
14
|
|
|
14
15
|
command -v jq >/dev/null 2>&1 || { echo "jq required" >&2; exit 1; }
|
|
15
16
|
|
|
16
|
-
# Resolve
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
# Resolve MCP config: per-project first, then global
|
|
18
|
+
KIMI_MCP=""
|
|
19
|
+
dir="$PWD"
|
|
20
|
+
while [ "$dir" != "/" ]; do
|
|
21
|
+
if [ -f "$dir/.kimi/mcp.json" ]; then
|
|
22
|
+
KIMI_MCP="$dir/.kimi/mcp.json"
|
|
23
|
+
break
|
|
24
|
+
fi
|
|
25
|
+
dir=$(dirname "$dir")
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
# Fallback to global config
|
|
29
|
+
if [ -z "$KIMI_MCP" ]; then
|
|
30
|
+
KIMI_MCP="${HOME}/.kimi/mcp.json"
|
|
31
|
+
fi
|
|
19
32
|
|
|
20
33
|
if [ ! -f "$KIMI_MCP" ]; then
|
|
21
|
-
echo "Kimi MCP config not found at $KIMI_MCP — run npx patchcord@latest first" >&2
|
|
34
|
+
echo "Kimi MCP config not found at $KIMI_MCP or any parent — run npx patchcord@latest first" >&2
|
|
22
35
|
exit 1
|
|
23
36
|
fi
|
|
24
37
|
|
|
@@ -45,7 +58,7 @@ if [ -z "$NAMESPACE_ID" ] || [ -z "$AGENT_ID" ]; then
|
|
|
45
58
|
fi
|
|
46
59
|
|
|
47
60
|
PIDFILE="/tmp/patchcord_subscribe_${NAMESPACE_ID}_${AGENT_ID}.pid"
|
|
48
|
-
NOTIFY_FILE="${
|
|
61
|
+
NOTIFY_FILE="${HOME}/.kimi/patchcord-subscribe-notify.txt"
|
|
49
62
|
|
|
50
63
|
# Pidfile guard: exit if another instance is running
|
|
51
64
|
if [ -f "$PIDFILE" ]; then
|