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 +8 -1
- package/examples/README.md +9 -6
- package/examples/auto-checkpoint.sh +38 -0
- package/index.mjs +67 -1
- package/package.json +1 -1
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?
|
|
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
|
|
package/examples/README.md
CHANGED
|
@@ -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
|
-
#
|
|
25
|
-
|
|
25
|
+
# One command — copies hook, updates settings.json, makes executable
|
|
26
|
+
npx cc-safe-setup --install-example block-database-wipe
|
|
27
|
+
```
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
chmod +x ~/.claude/hooks/block-database-wipe.sh
|
|
29
|
+
Or manually:
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
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.
|
|
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": {
|