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 +80 -0
- package/package.json +1 -1
- package/src/installer.js +120 -12
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
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
|
|
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 (
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
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 };
|