claude-code-notify-lite 1.0.5 → 1.0.7

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/cli.js CHANGED
@@ -31,6 +31,7 @@ Examples:
31
31
  $ ccnotify install Install hooks
32
32
  $ ccnotify test Test notification
33
33
  $ ccnotify status Check status
34
+ $ ccnotify repair Fix conflicting hooks
34
35
  $ ccnotify logs View debug logs
35
36
 
36
37
  Version: ${pkg.version}
@@ -118,6 +119,11 @@ program
118
119
  console.log(` Hook configured: ${status.hasHook ? chalk.green('Yes') : chalk.red('No')}`);
119
120
  console.log(` Config exists: ${status.hasConfig ? chalk.green('Yes') : chalk.red('No')}`);
120
121
 
122
+ if (status.hasConflicting) {
123
+ console.log(chalk.yellow('\n [WARN] Conflicting notification hooks detected'));
124
+ console.log(chalk.yellow(' Run "ccnotify repair" to fix'));
125
+ }
126
+
121
127
  console.log(chalk.gray(`\n Log file: ${logger.getLogPath()}`));
122
128
 
123
129
  if (!status.installed) {
@@ -125,6 +131,14 @@ program
125
131
  }
126
132
  });
127
133
 
134
+ program
135
+ .command('repair')
136
+ .description('Clean up conflicting hooks and reinstall')
137
+ .action(() => {
138
+ const { repair } = require('../src/installer');
139
+ repair();
140
+ });
141
+
128
142
  program
129
143
  .command('config')
130
144
  .description('Configure settings interactively')
@@ -296,4 +310,70 @@ program
296
310
  console.log('');
297
311
  });
298
312
 
313
+ program
314
+ .command('doctor')
315
+ .description('Diagnose hook configuration and show current settings')
316
+ .action(() => {
317
+ console.log(chalk.blue('Diagnosing claude-code-notify-lite...\n'));
318
+
319
+ const { getClaudeConfigDir } = safeRequire('../src/config', 'config');
320
+ const settingsPath = path.join(getClaudeConfigDir(), 'settings.json');
321
+
322
+ console.log(` Settings file: ${chalk.gray(settingsPath)}`);
323
+
324
+ if (!fs.existsSync(settingsPath)) {
325
+ console.log(chalk.yellow(' [!] Settings file not found'));
326
+ console.log(chalk.yellow(' Run "ccnotify install" to create hooks\n'));
327
+ return;
328
+ }
329
+
330
+ try {
331
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
332
+
333
+ if (!settings.hooks || !settings.hooks.Stop) {
334
+ console.log(chalk.yellow(' [!] No Stop hooks configured'));
335
+ console.log(chalk.yellow(' Run "ccnotify install" to create hooks\n'));
336
+ return;
337
+ }
338
+
339
+ console.log(chalk.green(' [OK] Stop hooks found\n'));
340
+ console.log(chalk.blue(' Current Stop hooks:'));
341
+
342
+ let hookCount = 0;
343
+ let hasValidHook = false;
344
+
345
+ settings.hooks.Stop.forEach((hookGroup, i) => {
346
+ if (hookGroup.hooks) {
347
+ hookGroup.hooks.forEach((hook, j) => {
348
+ hookCount++;
349
+ const cmd = hook.command || '(no command)';
350
+ const isValid = cmd.includes('cli.js') || cmd.includes('run.js') || cmd.includes('ccnotify');
351
+
352
+ if (isValid) {
353
+ hasValidHook = true;
354
+ console.log(chalk.green(` ${hookCount}. [VALID] ${cmd}`));
355
+ } else {
356
+ console.log(chalk.red(` ${hookCount}. [INVALID] ${cmd}`));
357
+ }
358
+ });
359
+ }
360
+ });
361
+
362
+ console.log('');
363
+
364
+ if (!hasValidHook) {
365
+ console.log(chalk.red(' [ERROR] No valid notification hooks found'));
366
+ console.log(chalk.yellow(' The hook command appears incomplete or corrupted'));
367
+ console.log(chalk.yellow(' Run "ccnotify repair" to fix\n'));
368
+ } else if (hookCount > 1) {
369
+ console.log(chalk.yellow(` [WARN] Multiple hooks detected (${hookCount})`));
370
+ console.log(chalk.yellow(' Run "ccnotify repair" to clean up\n'));
371
+ } else {
372
+ console.log(chalk.green(' [OK] Configuration looks good\n'));
373
+ }
374
+ } catch (err) {
375
+ console.log(chalk.red(` [ERROR] Failed to parse settings: ${err.message}\n`));
376
+ }
377
+ });
378
+
299
379
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-notify-lite",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Task completion notifications for Claude Code - Cross-platform, lightweight, and easy to use",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/installer.js CHANGED
@@ -79,6 +79,11 @@ function getHookCommand() {
79
79
  const nodePath = process.execPath;
80
80
  const originalCliPath = path.resolve(__dirname, '..', 'bin', 'cli.js');
81
81
 
82
+ if (!fs.existsSync(originalCliPath)) {
83
+ logger.error('CLI script not found', { originalCliPath });
84
+ throw new Error(`CLI script not found: ${originalCliPath}`);
85
+ }
86
+
82
87
  let cliPath = originalCliPath;
83
88
  let useLocalCopy = false;
84
89
  let command;
@@ -93,6 +98,11 @@ function getHookCommand() {
93
98
  command = `"${nodePath}" "${cliPath}" run`;
94
99
  }
95
100
 
101
+ if (!command.includes('cli.js') && !command.includes('run.js')) {
102
+ logger.error('Invalid hook command generated', { command, nodePath, cliPath });
103
+ throw new Error(`Invalid hook command: ${command}`);
104
+ }
105
+
96
106
  logger.info('Using absolute path command', { command, nodePath, cliPath, useLocalCopy });
97
107
  return command;
98
108
  }
@@ -151,6 +161,22 @@ function isOurHook(command) {
151
161
  command.includes('notify.js');
152
162
  }
153
163
 
164
+ function isNotificationHook(command) {
165
+ if (!command) return false;
166
+ const patterns = [
167
+ 'claude-code-notify-lite',
168
+ 'ccnotify',
169
+ 'notify.js',
170
+ 'notify.bat',
171
+ 'notify.ps1',
172
+ 'task-complete-notify',
173
+ 'notification',
174
+ 'notifier'
175
+ ];
176
+ const lowerCommand = command.toLowerCase();
177
+ return patterns.some(p => lowerCommand.includes(p.toLowerCase()));
178
+ }
179
+
154
180
  function install(options = {}) {
155
181
  console.log('Installing claude-code-notify-lite...\n');
156
182
  logger.info('Install started', { options });
@@ -195,22 +221,36 @@ function install(options = {}) {
195
221
  settings.hooks.Stop = [stopHook];
196
222
  logger.info('Created new Stop hook');
197
223
  } else {
198
- const existingIndex = settings.hooks.Stop.findIndex(h =>
199
- h.hooks && h.hooks.some(hh => isOurHook(hh.command))
224
+ const conflictingHooks = settings.hooks.Stop.filter(h =>
225
+ h.hooks && h.hooks.some(hh => isNotificationHook(hh.command) && !isOurHook(hh.command))
200
226
  );
201
227
 
202
- if (existingIndex === -1) {
203
- settings.hooks.Stop.push(stopHook);
204
- logger.info('Added Stop hook');
205
- } else {
206
- settings.hooks.Stop[existingIndex] = stopHook;
207
- console.log(' [OK] Updated existing hook');
208
- logger.info('Updated existing Stop hook');
228
+ if (conflictingHooks.length > 0) {
229
+ console.log(' [WARN] Found existing notification hooks:');
230
+ conflictingHooks.forEach(h => {
231
+ h.hooks.forEach(hh => {
232
+ if (isNotificationHook(hh.command)) {
233
+ console.log(` - ${hh.command}`);
234
+ }
235
+ });
236
+ });
237
+ console.log(' [INFO] Replacing with claude-code-notify-lite...');
238
+ logger.info('Found conflicting hooks, replacing them');
209
239
  }
240
+
241
+ settings.hooks.Stop = settings.hooks.Stop.filter(h => {
242
+ if (!h.hooks) return true;
243
+ h.hooks = h.hooks.filter(hh => !isNotificationHook(hh.command));
244
+ return h.hooks.length > 0;
245
+ });
246
+
247
+ settings.hooks.Stop.push(stopHook);
248
+ logger.info('Replaced notification hooks with our hook');
210
249
  }
211
250
 
212
251
  writeClaudeSettings(settings);
213
252
  console.log(' [OK] Configured Claude Code hooks');
253
+ console.log(` [OK] Hook command: ${hookCommand}`);
214
254
 
215
255
  console.log('\nInstallation complete!');
216
256
  console.log('Run "ccnotify test" or "npx ccnotify test" to verify.\n');
@@ -284,16 +324,84 @@ function checkInstallation() {
284
324
  h.hooks && h.hooks.some(hh => isOurHook(hh.command))
285
325
  );
286
326
 
327
+ const hasConflicting = settings.hooks &&
328
+ settings.hooks.Stop &&
329
+ settings.hooks.Stop.some(h =>
330
+ h.hooks && h.hooks.some(hh => isNotificationHook(hh.command) && !isOurHook(hh.command))
331
+ );
332
+
287
333
  const configDir = getConfigDir();
288
334
  const hasConfig = fs.existsSync(path.join(configDir, 'config.json'));
289
335
 
290
- logger.debug('Installation check', { hasHook, hasConfig });
336
+ logger.debug('Installation check', { hasHook, hasConfig, hasConflicting });
291
337
 
292
338
  return {
293
339
  installed: hasHook && hasConfig,
294
340
  hasHook,
295
- hasConfig
341
+ hasConfig,
342
+ hasConflicting
296
343
  };
297
344
  }
298
345
 
299
- module.exports = { install, uninstall, checkInstallation };
346
+ function repair() {
347
+ console.log('Repairing claude-code-notify-lite...\n');
348
+ logger.info('Repair started');
349
+
350
+ try {
351
+ const settings = readClaudeSettings();
352
+
353
+ if (!settings.hooks || !settings.hooks.Stop) {
354
+ console.log(' [INFO] No Stop hooks found');
355
+ console.log(' [INFO] Running fresh install...\n');
356
+ return install();
357
+ }
358
+
359
+ let removedCount = 0;
360
+ const removedHooks = [];
361
+
362
+ settings.hooks.Stop.forEach(h => {
363
+ if (h.hooks) {
364
+ h.hooks.forEach(hh => {
365
+ if (isNotificationHook(hh.command)) {
366
+ removedHooks.push(hh.command);
367
+ removedCount++;
368
+ }
369
+ });
370
+ }
371
+ });
372
+
373
+ if (removedCount > 0) {
374
+ console.log(' [INFO] Found notification hooks to clean:');
375
+ removedHooks.forEach(cmd => console.log(` - ${cmd}`));
376
+
377
+ settings.hooks.Stop = settings.hooks.Stop.filter(h => {
378
+ if (!h.hooks) return true;
379
+ h.hooks = h.hooks.filter(hh => !isNotificationHook(hh.command));
380
+ return h.hooks.length > 0;
381
+ });
382
+
383
+ if (settings.hooks.Stop.length === 0) {
384
+ delete settings.hooks.Stop;
385
+ }
386
+
387
+ if (Object.keys(settings.hooks).length === 0) {
388
+ delete settings.hooks;
389
+ }
390
+
391
+ writeClaudeSettings(settings);
392
+ console.log(` [OK] Removed ${removedCount} notification hook(s)`);
393
+ logger.info('Cleaned notification hooks', { removedCount, removedHooks });
394
+ } else {
395
+ console.log(' [INFO] No conflicting hooks found');
396
+ }
397
+
398
+ console.log(' [INFO] Reinstalling...\n');
399
+ return install();
400
+ } catch (err) {
401
+ logger.error('Repair failed', err);
402
+ console.error(` [ERROR] ${err.message}`);
403
+ return { success: false, error: err.message };
404
+ }
405
+ }
406
+
407
+ module.exports = { install, uninstall, checkInstallation, repair };