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 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(".codex") ? "Codex"
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: global ~/.kimi/mcp.json
1705
- const kimiPath = join(HOME, ".kimi", "mcp.json");
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(` ${yellow}Global config all Kimi CLI projects share this agent.${r}`);
1735
+ console.log(` ${dim}Per-projectuse 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.5.66",
3
+ "version": "0.5.67",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "author": "ppravdin",
6
6
  "license": "MIT",
@@ -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
- `~/.kimi/mcp.json`. There is no Patchcord plugin for Kimi behavior comes
13
- from MCP tools + this skill.
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
- Your identity is **global** (all Kimi projects share the same agent) because
16
- Kimi stores MCP config in `~/.kimi/mcp.json`.
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. Do the work - use real code, real files, real results from your project
101
- 3. Reply with the right flag:
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
- 4. If sender is online: `wait_for_message()` for follow-ups
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 bearer from ~/.kimi/mcp.json
18
- HOME_DIR="${HOME}"
19
- KIMI_MCP="${HOME_DIR}/.kimi/mcp.json"
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
- # Installed to ~/.kimi/patchcord-subscribe.sh by `npx patchcord@latest`.
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 auth from ~/.kimi/mcp.json
17
- HOME_DIR="${HOME}"
18
- KIMI_MCP="${HOME_DIR}/.kimi/mcp.json"
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="${HOME_DIR}/.kimi/patchcord-subscribe-notify.txt"
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