patchcord 0.5.72 → 0.5.74

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
@@ -1031,9 +1031,9 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1031
1031
  cpSync(join(pluginRoot, "per-project-skills", "kimi", "wait", "SKILL.md"), join(kimiWaitDir, "SKILL.md"));
1032
1032
  kimiChanged = true;
1033
1033
  }
1034
- if (kimiChanged) globalChanges.push("Kimi CLI skills installed");
1035
1034
 
1036
1035
  // Install/update stop hook — fires after each Kimi turn to check inbox
1036
+ let hookChanged = false;
1037
1037
  const kimiHookSrc = join(pluginRoot, "scripts", "kimi-stop-hook.sh");
1038
1038
  const kimiHookDest = join(HOME, ".kimi", "patchcord-stop-hook.sh");
1039
1039
  if (existsSync(kimiHookSrc)) {
@@ -1057,17 +1057,26 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1057
1057
 
1058
1058
  mkdirSync(dirname(kimiConfigPath), { recursive: true });
1059
1059
  writeFileSync(kimiConfigPath, kimiConfig);
1060
- globalChanges.push(`Kimi stop hook ${hookAlreadyExisted ? "updated" : "installed"}`);
1060
+ hookChanged = true;
1061
1061
  }
1062
1062
 
1063
1063
  // Install/update Kimi subscribe script (background polling)
1064
+ let subChanged = false;
1064
1065
  const kimiSubSrc = join(pluginRoot, "scripts", "kimi-subscribe.sh");
1065
1066
  const kimiSubDest = join(HOME, ".kimi", "patchcord-subscribe.sh");
1066
1067
  if (existsSync(kimiSubSrc)) {
1067
1068
  const subAlreadyExisted = existsSync(kimiSubDest);
1068
1069
  copyFileSync(kimiSubSrc, kimiSubDest);
1069
1070
  chmodSync(kimiSubDest, 0o755);
1070
- globalChanges.push(`Kimi subscribe script ${subAlreadyExisted ? "updated" : "installed"}`);
1071
+ subChanged = true;
1072
+ }
1073
+
1074
+ const kimiParts = [];
1075
+ if (kimiChanged) kimiParts.push("skills");
1076
+ if (hookChanged) kimiParts.push("stop hook");
1077
+ if (subChanged) kimiParts.push("subscribe script");
1078
+ if (kimiParts.length > 0) {
1079
+ globalChanges.push(`Kimi CLI ${kimiParts.join(" + ")} installed`);
1071
1080
  }
1072
1081
  }
1073
1082
 
@@ -1133,6 +1142,7 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1133
1142
 
1134
1143
  if (!hasClaude && !existsSync(codexConfig) && !hasKimi) {
1135
1144
  console.log(`${dim}No Claude Code, Codex CLI, or Kimi CLI detected — skipping global setup.${r}`);
1145
+ console.log(`${dim} Kimi CLI: https://kimi.moonshot.cn/download${r}`);
1136
1146
  }
1137
1147
 
1138
1148
  if (updateOnly) process.exit(0);
@@ -1817,16 +1827,21 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1817
1827
  }
1818
1828
  if (aliasInstalled) {
1819
1829
  console.log(`\n ${green}✓${r} Alias installed in ${primaryName}`);
1820
- console.log(` ${dim}Run ${bold}kimi-pc${r}${dim} instead of ${bold}kimi${r}${dim} in project directories.${r}`);
1821
- console.log(` ${dim}Reload your shell or run ${cyan}source ~/${primaryName}${r}${dim} to use it now.${r}`);
1822
1830
  } else if (aliasAlreadyPresent) {
1823
- console.log(`\n ${green}✓${r} Alias already present in shell config`);
1824
- console.log(` ${dim}Run ${bold}kimi-pc${r}${dim} instead of ${bold}kimi${r}${dim} in project directories.${r}`);
1831
+ console.log(`\n ${green}✓${r} Alias already present in ${primaryName}`);
1825
1832
  } else {
1826
1833
  console.log(`\n ${yellow}⚠${r} No shell config found (~/.bashrc or ~/.zshrc).`);
1827
1834
  console.log(` ${dim}Add this to your ~/${primaryName} manually:${r}`);
1828
1835
  console.log(` ${cyan}${aliasLine}${r}`);
1829
1836
  }
1837
+ // Prominent kimi-pc reminder — users MUST run kimi-pc instead of kimi
1838
+ console.log(`\n ${yellow}┌─────────────────────────────────────────────────────────────┐${r}`);
1839
+ console.log(` ${yellow}│${r} ${bold}IMPORTANT:${r} Run ${white}${bold}kimi-pc${r} instead of ${dim}kimi${r} in this project ${yellow}│${r}`);
1840
+ console.log(` ${yellow}│${r} ${dim}kimi-pc loads the per-project agent from .kimi/mcp.json${r} ${yellow}│${r}`);
1841
+ if (aliasInstalled) {
1842
+ console.log(` ${yellow}│${r} Reload shell: ${cyan}source ~/${primaryName}${r} ${yellow}│${r}`);
1843
+ }
1844
+ console.log(` ${yellow}└─────────────────────────────────────────────────────────────┘${r}`);
1830
1845
  } else if (isVSCode) {
1831
1846
  // VS Code: write .vscode/mcp.json (per-project)
1832
1847
  const vscodeDir = join(cwd, ".vscode");
@@ -2080,6 +2095,8 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
2080
2095
 
2081
2096
  if (isOpenClaw) {
2082
2097
  console.log(`\n${dim}Run${r} ${bold}openclaw gateway restart${r}${dim}, then tools will be available in your channels.${r}`);
2098
+ } else if (isKimi) {
2099
+ console.log(`\n ${green}→${r} ${bold}Restart your ${toolName} session with ${cyan}kimi-pc${r}${bold}, then say: ${cyan}${bold}check inbox${r}`);
2083
2100
  } else {
2084
2101
  console.log(`\n ${green}→${r} ${bold}Restart your ${toolName} session${r}, then say: ${cyan}${bold}check inbox${r}`);
2085
2102
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.5.72",
3
+ "version": "0.5.74",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "author": "ppravdin",
6
6
  "license": "MIT",
@@ -6,6 +6,9 @@ set -euo pipefail
6
6
  #
7
7
  # Supports per-project .kimi/mcp.json (walks up from cwd) and
8
8
  # falls back to global ~/.kimi/mcp.json.
9
+ #
10
+ # CRITICAL: Kimi v1.44.0 ONLY acts on Stop hooks when they BLOCK (exit code 2).
11
+ # stdout is silently discarded. stderr becomes the "reason" that triggers a new turn.
9
12
 
10
13
  command -v jq >/dev/null 2>&1 || exit 0
11
14
 
@@ -13,9 +16,7 @@ INPUT=$(cat)
13
16
 
14
17
  # Guard: stop_hook_active prevents infinite loops
15
18
  STOP_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false' 2>/dev/null || echo "false")
16
- if [ "$STOP_ACTIVE" = "true" ]; then
17
- exit 0
18
- fi
19
+ [ "$STOP_ACTIVE" = "true" ] && exit 0
19
20
 
20
21
  # Resolve MCP config: per-project first, then global
21
22
  KIMI_MCP=""
@@ -33,9 +34,7 @@ if [ -n "$CWD" ]; then
33
34
  fi
34
35
 
35
36
  # Fallback to global config
36
- if [ -z "$KIMI_MCP" ]; then
37
- KIMI_MCP="${HOME}/.kimi/mcp.json"
38
- fi
37
+ [ -z "$KIMI_MCP" ] && KIMI_MCP="${HOME}/.kimi/mcp.json"
39
38
 
40
39
  TOKEN=""
41
40
  URL=""
@@ -45,9 +44,7 @@ if [ -f "$KIMI_MCP" ]; then
45
44
  URL=$(jq -r '.mcpServers.patchcord.url // empty' "$KIMI_MCP" 2>/dev/null || true)
46
45
  fi
47
46
 
48
- if [ -z "$URL" ] || [ -z "$TOKEN" ]; then
49
- exit 0
50
- fi
47
+ [ -z "$URL" ] || [ -z "$TOKEN" ] && exit 0
51
48
 
52
49
  # Normalize URL to base
53
50
  BASE_URL=$(echo "$URL" | sed 's|/mcp$||; s|/mcp/bearer$||')
@@ -57,26 +54,26 @@ HTTP_CODE=$(curl -s -o /tmp/patchcord_kimi_inbox.json -w "%{http_code}" --max-ti
57
54
  -H "Authorization: Bearer ${TOKEN}" \
58
55
  "${BASE_URL}/api/inbox?status=pending&limit=5&count_only=1" 2>/dev/null || echo "000")
59
56
 
60
- if [ "$HTTP_CODE" != "200" ]; then
61
- rm -f /tmp/patchcord_kimi_inbox.json
62
- exit 0
63
- fi
57
+ [ "$HTTP_CODE" != "200" ] && { rm -f /tmp/patchcord_kimi_inbox.json; exit 0; }
64
58
 
65
59
  RESPONSE=$(cat /tmp/patchcord_kimi_inbox.json 2>/dev/null || echo '{"count":0}')
66
60
  rm -f /tmp/patchcord_kimi_inbox.json
67
61
 
68
62
  COUNT=$(echo "$RESPONSE" | jq -r '.count // .pending_count // 0' 2>/dev/null || echo "0")
69
63
 
70
- if [ "$COUNT" -gt 0 ]; then
71
- # Deduplicate: only notify once every 30 seconds
72
- NOTIFY_LOCK="/tmp/patchcord_kimi_notify_lock"
73
- if [ -f "$NOTIFY_LOCK" ]; then
74
- LOCK_MTIME=$(stat -c %Y "$NOTIFY_LOCK" 2>/dev/null || stat -f %m "$NOTIFY_LOCK" 2>/dev/null || echo "0")
75
- NOW=$(date +%s)
76
- [ $(( NOW - LOCK_MTIME )) -lt 30 ] && exit 0
77
- fi
78
- touch "$NOTIFY_LOCK"
79
- echo "📬 Patchcord: You have ${COUNT} pending message(s). Call inbox() to read and reply."
64
+ [ "$COUNT" -gt 0 ] || exit 0
65
+
66
+ # Deduplicate: only notify once every 30 seconds
67
+ NOTIFY_LOCK="/tmp/patchcord_kimi_notify_lock"
68
+ if [ -f "$NOTIFY_LOCK" ]; then
69
+ LOCK_MTIME=$(stat -c %Y "$NOTIFY_LOCK" 2>/dev/null || stat -f %m "$NOTIFY_LOCK" 2>/dev/null || echo "0")
70
+ NOW=$(date +%s)
71
+ [ $(( NOW - LOCK_MTIME )) -lt 30 ] && exit 0
80
72
  fi
81
73
 
82
- exit 0
74
+ touch "$NOTIFY_LOCK"
75
+
76
+ # Exit 2 + stderr = Kimi starts a new turn with this as the user message.
77
+ # stdout is silently discarded by Kimi for Stop hooks.
78
+ printf '%s\n' "📬 Patchcord: You have ${COUNT} pending message(s). Call inbox() to read and reply." >&2
79
+ exit 2