claude-rpc 1.1.2 → 1.1.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-rpc",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Discord Rich Presence for Claude Code — live model, project, tokens, and lifetime stats driven by Claude Code's hook system.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/doctor.js CHANGED
@@ -9,7 +9,7 @@ import { existsSync, readFileSync, statSync, readdirSync } from 'node:fs';
9
9
  import { join, basename } from 'node:path';
10
10
  import {
11
11
  IS_PACKAGED, IS_NPM_INSTALL, IS_INSTALLED,
12
- CONFIG_PATH, CANONICAL_EXE, USER_CONFIG_DIR,
12
+ CONFIG_PATH, CANONICAL_EXE, USER_CONFIG_DIR, HOOK_SCRIPT,
13
13
  STATE_PATH, PID_PATH, LOG_PATH,
14
14
  AGGREGATE_PATH, SCAN_CACHE_PATH,
15
15
  CLAUDE_HOME, CLAUDE_PROJECTS, CLAUDE_SETTINGS,
@@ -95,6 +95,35 @@ export function classifyClientId(clientId) {
95
95
  return 'ok';
96
96
  }
97
97
 
98
+ // One wired hook command, classified against the running install. installHooks
99
+ // has written `"<abs node>" "<abs hook.js>" <event>` (npm + dev) or
100
+ // `"<canonical exe>" hook <event>` (packaged) since v0.19.1 — absolute on both
101
+ // tokens, because Claude Code runs hooks through a minimal-PATH shell that,
102
+ // under a version manager, has neither `claude-rpc` nor `node` on it.
103
+ // 'legacy' — PATH-dependent launcher (pre-v0.19.1 `claude-rpc hook <event>` /
104
+ // bare `node "<hook.js>"`): fails as `claude-rpc: command not
105
+ // found` on every tool use.
106
+ // 'dead' — absolute launcher no longer on disk (e.g. nvm pruned the node
107
+ // version the hooks were wired against).
108
+ // 'stale' — runs, but points at a different install than this binary.
109
+ // 'ok' — wired against the current install.
110
+ export function classifyHookCommand(cmd, {
111
+ packaged = IS_PACKAGED,
112
+ npm = IS_NPM_INSTALL,
113
+ canonicalExe = CANONICAL_EXE,
114
+ hookScript = HOOK_SCRIPT,
115
+ exists = existsSync,
116
+ } = {}) {
117
+ const c = String(cmd || '');
118
+ const m = c.match(/^"([^"]+)"|^(\S+)/);
119
+ const launcher = m ? (m[1] ?? m[2]) : '';
120
+ if (launcher && !launcher.includes('/') && !launcher.includes('\\')) return 'legacy';
121
+ if (launcher && !exists(launcher)) return 'dead';
122
+ if (packaged && !c.includes(canonicalExe)) return 'stale';
123
+ if (npm && !c.includes(hookScript.replace(/\\/g, '/'))) return 'stale';
124
+ return 'ok';
125
+ }
126
+
98
127
  // Most-recent-wins IPC state inferred from the daemon log tail: 'up'/'down'/'unknown'.
99
128
  export function ipcStateFromLog(logText) {
100
129
  let ipc = 'unknown';
@@ -192,26 +221,34 @@ function checkHooks() {
192
221
  }
193
222
  const missing = [];
194
223
  const stale = [];
224
+ const broken = [];
195
225
  for (const event of HOOK_EVENTS) {
196
226
  const bucket = settings.hooks?.[event];
197
227
  if (!Array.isArray(bucket) || bucket.length === 0) { missing.push(event); continue; }
198
228
  const ours = bucket.flatMap((e) => e.hooks || []).find((h) => isOurHook(h));
199
229
  if (!ours) { missing.push(event); continue; }
200
- if (IS_PACKAGED && !ours.command.includes(CANONICAL_EXE)) stale.push({ event, cmd: ours.command });
201
- if (IS_NPM_INSTALL && !/\bclaude-rpc\b\s+hook\b/.test(ours.command)) stale.push({ event, cmd: ours.command });
230
+ const cls = classifyHookCommand(ours.command);
231
+ if (cls === 'legacy' || cls === 'dead') broken.push(cls);
232
+ else if (cls === 'stale') stale.push(event);
202
233
  }
203
- if (missing.length === 0 && stale.length === 0) {
234
+ if (missing.length === 0 && stale.length === 0 && broken.length === 0) {
204
235
  check(`hooks registered (${HOOK_EVENTS.length}/${HOOK_EVENTS.length})`, 'pass',
205
236
  'all events wired against the current binary');
206
237
  } else if (missing.length === HOOK_EVENTS.length) {
207
238
  check('hooks registered', 'fail', 'no claude-rpc hooks found',
208
239
  'run `claude-rpc setup` to register hooks', 'setup');
240
+ } else if (broken.length > 0) {
241
+ check('hooks registered', 'fail',
242
+ broken.includes('legacy')
243
+ ? `${broken.length} PATH-dependent (pre-v0.19.1) — fail as \`claude-rpc: command not found\` under Claude Code's hook shell`
244
+ : `${broken.length} pointing at a launcher that's gone from disk`,
245
+ 'run `claude-rpc setup` to rewrite hooks as absolute, PATH-independent commands', 'setup');
209
246
  } else if (missing.length > 0) {
210
247
  check('hooks registered', 'warn', `missing: ${missing.join(', ')}`,
211
248
  'run `claude-rpc setup` to add the missing events', 'setup');
212
249
  } else if (stale.length > 0) {
213
250
  check('hooks registered', 'warn',
214
- `${stale.length} pointing at an old binary path`,
251
+ `${stale.length} wired against a different install than this binary`,
215
252
  'run `claude-rpc setup` to refresh hook commands against the current binary', 'setup');
216
253
  }
217
254
  }
package/src/version.js CHANGED
@@ -11,7 +11,7 @@ import { readFileSync } from 'node:fs';
11
11
  import { join } from 'node:path';
12
12
  import { ROOT } from './paths.js';
13
13
 
14
- const BAKED = '1.1.2';
14
+ const BAKED = '1.1.4';
15
15
 
16
16
  function readPkgVersion() {
17
17
  try {