cc-safe-setup 1.8.5 → 1.9.0

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.
Files changed (2) hide show
  1. package/index.mjs +66 -1
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync } from 'fs';
3
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync, copyFileSync } from 'fs';
4
4
  import { join, dirname } from 'path';
5
5
  import { homedir } from 'os';
6
6
  import { createInterface } from 'readline';
@@ -66,6 +66,8 @@ const HELP = process.argv.includes('--help') || process.argv.includes('-h');
66
66
  const STATUS = process.argv.includes('--status') || process.argv.includes('-s');
67
67
  const VERIFY = process.argv.includes('--verify') || process.argv.includes('-v');
68
68
  const EXAMPLES = process.argv.includes('--examples') || process.argv.includes('-e');
69
+ const INSTALL_EXAMPLE_IDX = process.argv.findIndex(a => a === '--install-example');
70
+ const INSTALL_EXAMPLE = INSTALL_EXAMPLE_IDX !== -1 ? process.argv[INSTALL_EXAMPLE_IDX + 1] : null;
69
71
 
70
72
  if (HELP) {
71
73
  console.log(`
@@ -78,6 +80,7 @@ if (HELP) {
78
80
  npx cc-safe-setup --dry-run Preview without installing
79
81
  npx cc-safe-setup --uninstall Remove all installed hooks
80
82
  npx cc-safe-setup --examples List available example hooks
83
+ npx cc-safe-setup --install-example <name> Install a specific example hook
81
84
  npx cc-safe-setup --help Show this help
82
85
 
83
86
  Hooks installed:
@@ -286,11 +289,73 @@ function examples() {
286
289
  console.log();
287
290
  }
288
291
 
292
+ async function installExample(name) {
293
+ const examplesDir = join(__dirname, 'examples');
294
+ const filename = name.endsWith('.sh') ? name : name + '.sh';
295
+ const srcPath = join(examplesDir, filename);
296
+
297
+ if (!existsSync(srcPath)) {
298
+ console.log();
299
+ console.log(c.red + ' Error: example "' + name + '" not found.' + c.reset);
300
+ console.log(c.dim + ' Run --examples to see available hooks.' + c.reset);
301
+ console.log();
302
+ process.exit(1);
303
+ }
304
+
305
+ const destPath = join(HOOKS_DIR, filename);
306
+ mkdirSync(HOOKS_DIR, { recursive: true });
307
+ copyFileSync(srcPath, destPath);
308
+ chmodSync(destPath, 0o755);
309
+
310
+ // Parse hook header for matcher and trigger
311
+ const content = readFileSync(srcPath, 'utf8');
312
+ let trigger = 'PreToolUse';
313
+ let matcher = 'Bash';
314
+
315
+ // Detect trigger from header comments
316
+ if (content.includes('PostToolUse')) trigger = 'PostToolUse';
317
+ if (content.includes('Notification')) trigger = 'Notification';
318
+ if (content.includes('Stop')) trigger = 'Stop';
319
+
320
+ // Detect matcher from header
321
+ const matcherMatch = content.match(/"matcher":\s*"([^"]*)"/);
322
+ if (matcherMatch) matcher = matcherMatch[1];
323
+
324
+ // Update settings.json
325
+ let settings = {};
326
+ if (existsSync(SETTINGS_PATH)) {
327
+ settings = JSON.parse(readFileSync(SETTINGS_PATH, 'utf8'));
328
+ }
329
+ if (!settings.hooks) settings.hooks = {};
330
+ if (!settings.hooks[trigger]) settings.hooks[trigger] = [];
331
+
332
+ const hookEntry = {
333
+ matcher: matcher,
334
+ hooks: [{ type: 'command', command: destPath }],
335
+ };
336
+
337
+ // Check if already installed
338
+ const existing = settings.hooks[trigger].find(h =>
339
+ h.hooks && h.hooks.some(hh => hh.command && hh.command.includes(filename))
340
+ );
341
+ if (!existing) {
342
+ settings.hooks[trigger].push(hookEntry);
343
+ writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
344
+ }
345
+
346
+ console.log();
347
+ console.log(c.green + ' ✓' + c.reset + ' Installed ' + c.bold + filename + c.reset);
348
+ console.log(c.dim + ' → ' + destPath + c.reset);
349
+ console.log(c.dim + ' → settings.json updated (' + trigger + ', matcher: "' + matcher + '")' + c.reset);
350
+ console.log();
351
+ }
352
+
289
353
  async function main() {
290
354
  if (UNINSTALL) return uninstall();
291
355
  if (VERIFY) return verify();
292
356
  if (STATUS) return status();
293
357
  if (EXAMPLES) return examples();
358
+ if (INSTALL_EXAMPLE) return installExample(INSTALL_EXAMPLE);
294
359
 
295
360
  console.log();
296
361
  console.log(c.bold + ' cc-safe-setup' + c.reset);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-safe-setup",
3
- "version": "1.8.5",
3
+ "version": "1.9.0",
4
4
  "description": "One command to make Claude Code safe for autonomous operation. 8 hooks: destructive blocker, branch guard, force-push protection, secret leak prevention, syntax checks, and more.",
5
5
  "main": "index.mjs",
6
6
  "bin": {