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.
- package/index.mjs +66 -1
- 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.
|
|
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": {
|