cc-safe-setup 1.8.5 → 1.9.1

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/README.md CHANGED
@@ -128,7 +128,13 @@ Or start with the free hooks: [claude-code-hooks](https://github.com/yurukusa/cl
128
128
 
129
129
  ## Examples
130
130
 
131
- Need custom hooks beyond the 8 built-in ones? See [`examples/`](examples/) for ready-to-use recipes:
131
+ Need custom hooks beyond the 8 built-in ones? Install any example with one command:
132
+
133
+ ```bash
134
+ npx cc-safe-setup --install-example block-database-wipe
135
+ ```
136
+
137
+ Or browse all available examples in [`examples/`](examples/):
132
138
 
133
139
  - **auto-approve-git-read.sh** — Auto-approve `git status`, `git log`, even with `-C` flags
134
140
  - **auto-approve-ssh.sh** — Auto-approve safe SSH commands (`uptime`, `whoami`, etc.)
@@ -143,6 +149,7 @@ Need custom hooks beyond the 8 built-in ones? See [`examples/`](examples/) for r
143
149
  - **allowlist.sh** — Block everything not explicitly approved — inverse permission model ([#37471](https://github.com/anthropics/claude-code/issues/37471))
144
150
  - **protect-dotfiles.sh** — Block modifications to `~/.bashrc`, `~/.aws/`, `~/.ssh/` and chezmoi without diff ([#37478](https://github.com/anthropics/claude-code/issues/37478))
145
151
  - **scope-guard.sh** — Block file operations outside project directory — absolute paths, home, parent escapes ([#36233](https://github.com/anthropics/claude-code/issues/36233))
152
+ - **auto-checkpoint.sh** — Auto-commit after every edit for rollback protection ([#34674](https://github.com/anthropics/claude-code/issues/34674))
146
153
 
147
154
  ## Learn More
148
155
 
@@ -5,6 +5,7 @@ Custom hooks beyond the 8 built-in ones. Copy any file to `~/.claude/hooks/` and
5
5
  | Hook | Purpose | Related Issue |
6
6
  |------|---------|---------------|
7
7
  | **allowlist.sh** | Block everything not explicitly approved (inverse model) | [#37471](https://github.com/anthropics/claude-code/issues/37471) |
8
+ | **auto-checkpoint.sh** | Auto-commit after edits for rollback protection | [#34674](https://github.com/anthropics/claude-code/issues/34674) |
8
9
  | **auto-approve-build.sh** | Auto-approve npm/yarn/cargo/go build, test, lint | |
9
10
  | **auto-approve-docker.sh** | Auto-approve docker build, compose, ps, logs | |
10
11
  | **auto-approve-git-read.sh** | Auto-approve `git status/log/diff` even with `-C` flags | [#36900](https://github.com/anthropics/claude-code/issues/36900) |
@@ -21,14 +22,16 @@ Custom hooks beyond the 8 built-in ones. Copy any file to `~/.claude/hooks/` and
21
22
  ## Quick Start
22
23
 
23
24
  ```bash
24
- # 1. Copy example to hooks directory
25
- cp examples/block-database-wipe.sh ~/.claude/hooks/
25
+ # One command copies hook, updates settings.json, makes executable
26
+ npx cc-safe-setup --install-example block-database-wipe
27
+ ```
26
28
 
27
- # 2. Make executable
28
- chmod +x ~/.claude/hooks/block-database-wipe.sh
29
+ Or manually:
29
30
 
30
- # 3. Add to settings.json
31
- # See each file's header comment for the JSON configuration
31
+ ```bash
32
+ cp examples/block-database-wipe.sh ~/.claude/hooks/
33
+ chmod +x ~/.claude/hooks/block-database-wipe.sh
34
+ # Add to settings.json — see each file's header for the JSON config
32
35
  ```
33
36
 
34
37
  ## List from CLI
@@ -0,0 +1,38 @@
1
+ #!/bin/bash
2
+ # auto-checkpoint.sh — Auto-commit after every edit for rollback protection
3
+ #
4
+ # Solves: Context compaction silently reverting uncommitted edits (#34674)
5
+ # Also protects against: session crashes, token expiry, any unexpected death
6
+ #
7
+ # Creates lightweight checkpoint commits after every Edit/Write.
8
+ # If anything goes wrong, you can recover with `git log` and `git cherry-pick`.
9
+ #
10
+ # Usage: Add to settings.json as a PostToolUse hook
11
+ #
12
+ # {
13
+ # "hooks": {
14
+ # "PostToolUse": [{
15
+ # "matcher": "Edit|Write",
16
+ # "hooks": [{ "type": "command", "command": "~/.claude/hooks/auto-checkpoint.sh" }]
17
+ # }]
18
+ # }
19
+ # }
20
+
21
+ INPUT=$(cat)
22
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
23
+
24
+ # Only checkpoint after Edit or Write
25
+ [[ "$TOOL" != "Edit" && "$TOOL" != "Write" ]] && exit 0
26
+
27
+ # Must be in a git repo
28
+ git rev-parse --git-dir &>/dev/null || exit 0
29
+
30
+ # Only commit if there are actual changes
31
+ DIRTY=$(git status --porcelain 2>/dev/null | head -1)
32
+ [[ -z "$DIRTY" ]] && exit 0
33
+
34
+ # Create checkpoint commit
35
+ git add -A 2>/dev/null
36
+ git commit -m "checkpoint: auto-save $(date +%H:%M:%S)" --no-verify 2>/dev/null
37
+
38
+ exit 0
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:
@@ -266,6 +269,7 @@ function examples() {
266
269
  'allowlist.sh': 'Block everything not in allowlist (inverse permission model)',
267
270
  'protect-dotfiles.sh': 'Block modifications to ~/.bashrc, ~/.aws/, ~/.ssh/',
268
271
  'scope-guard.sh': 'Block file operations outside project directory',
272
+ 'auto-checkpoint.sh': 'Auto-commit after edits for rollback protection',
269
273
  };
270
274
 
271
275
  console.log();
@@ -286,11 +290,73 @@ function examples() {
286
290
  console.log();
287
291
  }
288
292
 
293
+ async function installExample(name) {
294
+ const examplesDir = join(__dirname, 'examples');
295
+ const filename = name.endsWith('.sh') ? name : name + '.sh';
296
+ const srcPath = join(examplesDir, filename);
297
+
298
+ if (!existsSync(srcPath)) {
299
+ console.log();
300
+ console.log(c.red + ' Error: example "' + name + '" not found.' + c.reset);
301
+ console.log(c.dim + ' Run --examples to see available hooks.' + c.reset);
302
+ console.log();
303
+ process.exit(1);
304
+ }
305
+
306
+ const destPath = join(HOOKS_DIR, filename);
307
+ mkdirSync(HOOKS_DIR, { recursive: true });
308
+ copyFileSync(srcPath, destPath);
309
+ chmodSync(destPath, 0o755);
310
+
311
+ // Parse hook header for matcher and trigger
312
+ const content = readFileSync(srcPath, 'utf8');
313
+ let trigger = 'PreToolUse';
314
+ let matcher = 'Bash';
315
+
316
+ // Detect trigger from header comments
317
+ if (content.includes('PostToolUse')) trigger = 'PostToolUse';
318
+ if (content.includes('Notification')) trigger = 'Notification';
319
+ if (content.includes('Stop')) trigger = 'Stop';
320
+
321
+ // Detect matcher from header
322
+ const matcherMatch = content.match(/"matcher":\s*"([^"]*)"/);
323
+ if (matcherMatch) matcher = matcherMatch[1];
324
+
325
+ // Update settings.json
326
+ let settings = {};
327
+ if (existsSync(SETTINGS_PATH)) {
328
+ settings = JSON.parse(readFileSync(SETTINGS_PATH, 'utf8'));
329
+ }
330
+ if (!settings.hooks) settings.hooks = {};
331
+ if (!settings.hooks[trigger]) settings.hooks[trigger] = [];
332
+
333
+ const hookEntry = {
334
+ matcher: matcher,
335
+ hooks: [{ type: 'command', command: destPath }],
336
+ };
337
+
338
+ // Check if already installed
339
+ const existing = settings.hooks[trigger].find(h =>
340
+ h.hooks && h.hooks.some(hh => hh.command && hh.command.includes(filename))
341
+ );
342
+ if (!existing) {
343
+ settings.hooks[trigger].push(hookEntry);
344
+ writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2) + '\n');
345
+ }
346
+
347
+ console.log();
348
+ console.log(c.green + ' ✓' + c.reset + ' Installed ' + c.bold + filename + c.reset);
349
+ console.log(c.dim + ' → ' + destPath + c.reset);
350
+ console.log(c.dim + ' → settings.json updated (' + trigger + ', matcher: "' + matcher + '")' + c.reset);
351
+ console.log();
352
+ }
353
+
289
354
  async function main() {
290
355
  if (UNINSTALL) return uninstall();
291
356
  if (VERIFY) return verify();
292
357
  if (STATUS) return status();
293
358
  if (EXAMPLES) return examples();
359
+ if (INSTALL_EXAMPLE) return installExample(INSTALL_EXAMPLE);
294
360
 
295
361
  console.log();
296
362
  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.1",
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": {