cc-safe-setup 9.1.0 → 9.3.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/README.md +1 -1
- package/docs/hub.html +155 -0
- package/docs/matrix.html +139 -0
- package/docs/troubleshooting.html +189 -0
- package/examples/rust/destructive_guard.rs +72 -0
- package/index.mjs +86 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
**One command to make Claude Code safe for autonomous operation.** [日本語](docs/README.ja.md)
|
|
8
8
|
|
|
9
|
-
8 built-in + 104 examples = **112 hooks**. 34 CLI commands. 457 tests. 4 languages. [
|
|
9
|
+
8 built-in + 104 examples = **112 hooks**. 34 CLI commands. 457 tests. 4 languages. [**Hub**](https://yurukusa.github.io/cc-safe-setup/hub.html) · [Cheat Sheet](https://yurukusa.github.io/cc-safe-setup/hooks-cheatsheet.html) · [Builder](https://yurukusa.github.io/cc-safe-setup/builder.html) · [FAQ](https://yurukusa.github.io/cc-safe-setup/faq.html) · [Examples](https://yurukusa.github.io/cc-safe-setup/by-example.html) · [Matrix](https://yurukusa.github.io/cc-safe-setup/matrix.html) · [Playground](https://yurukusa.github.io/cc-hook-registry/playground.html)
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
npx cc-safe-setup
|
package/docs/hub.html
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>cc-safe-setup — Claude Code Safety Hub</title>
|
|
7
|
+
<meta name="description" content="112 hooks, 34 commands, 10 tools. Everything you need to make Claude Code safe.">
|
|
8
|
+
<style>
|
|
9
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
10
|
+
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#0d1117;color:#c9d1d9;min-height:100vh}
|
|
11
|
+
.hero{text-align:center;padding:3rem 1.5rem 2rem}
|
|
12
|
+
h1{color:#f0f6fc;font-size:2rem;margin-bottom:.5rem}
|
|
13
|
+
.tagline{color:#8b949e;font-size:1rem;margin-bottom:1.5rem}
|
|
14
|
+
.stats{display:flex;justify-content:center;gap:2rem;flex-wrap:wrap;margin:1rem 0}
|
|
15
|
+
.stat{text-align:center}
|
|
16
|
+
.stat-num{font-size:1.8rem;font-weight:700;color:#f0f6fc}
|
|
17
|
+
.stat-label{font-size:.75rem;color:#8b949e}
|
|
18
|
+
.install{background:#238636;color:#fff;padding:.7rem 1.5rem;border-radius:8px;font-family:monospace;font-size:1rem;border:none;cursor:pointer;margin:1.5rem 0;display:inline-block}
|
|
19
|
+
.install:hover{background:#2ea043}
|
|
20
|
+
.grid{max-width:1000px;margin:0 auto;padding:0 1.5rem 2rem;display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:.8rem}
|
|
21
|
+
.card{background:#161b22;border:1px solid #30363d;border-radius:8px;padding:1rem;transition:border-color .2s;text-decoration:none;color:inherit;display:block}
|
|
22
|
+
.card:hover{border-color:#58a6ff}
|
|
23
|
+
.card-icon{font-size:1.5rem;margin-bottom:.4rem}
|
|
24
|
+
.card-title{font-weight:700;color:#f0f6fc;font-size:.95rem;margin-bottom:.3rem}
|
|
25
|
+
.card-desc{color:#8b949e;font-size:.8rem;line-height:1.4}
|
|
26
|
+
.card-tag{display:inline-block;background:#21262d;color:#8b949e;font-size:.65rem;padding:.1rem .3rem;border-radius:3px;margin-top:.4rem}
|
|
27
|
+
.section-title{text-align:center;color:#f0f6fc;font-size:1.1rem;margin:2rem 0 1rem;padding-top:1rem;border-top:1px solid #21262d}
|
|
28
|
+
.footer{text-align:center;padding:2rem;color:#484f58;font-size:.75rem}
|
|
29
|
+
a{color:#58a6ff;text-decoration:none}
|
|
30
|
+
.start{max-width:600px;margin:0 auto;padding:0 1.5rem}
|
|
31
|
+
.start-step{display:flex;gap:.8rem;align-items:flex-start;margin:.5rem 0}
|
|
32
|
+
.start-num{background:#238636;color:#fff;width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.75rem;font-weight:700;flex-shrink:0}
|
|
33
|
+
.start-text{font-size:.85rem;color:#c9d1d9}
|
|
34
|
+
code{background:#21262d;padding:.15rem .3rem;border-radius:3px;font-size:.8rem}
|
|
35
|
+
</style>
|
|
36
|
+
</head>
|
|
37
|
+
<body>
|
|
38
|
+
|
|
39
|
+
<div class="hero">
|
|
40
|
+
<h1>cc-safe-setup</h1>
|
|
41
|
+
<div class="tagline">Make Claude Code safe for autonomous operation</div>
|
|
42
|
+
|
|
43
|
+
<div class="stats">
|
|
44
|
+
<div class="stat"><div class="stat-num">112</div><div class="stat-label">hooks</div></div>
|
|
45
|
+
<div class="stat"><div class="stat-num">34</div><div class="stat-label">commands</div></div>
|
|
46
|
+
<div class="stat"><div class="stat-num">457</div><div class="stat-label">tests</div></div>
|
|
47
|
+
<div class="stat"><div class="stat-num">10</div><div class="stat-label">web tools</div></div>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<button class="install" onclick="navigator.clipboard.writeText('npx cc-safe-setup --shield');this.textContent='Copied!';setTimeout(()=>this.textContent='npx cc-safe-setup --shield',1500)">npx cc-safe-setup --shield</button>
|
|
51
|
+
<div style="color:#8b949e;font-size:.8rem">One command. 30 seconds. Zero dependencies.</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div class="section-title">Get Started</div>
|
|
55
|
+
<div class="start">
|
|
56
|
+
<div class="start-step"><div class="start-num">1</div><div class="start-text">Run <code>npx cc-safe-setup --shield</code> — installs hooks, detects your stack, generates CLAUDE.md</div></div>
|
|
57
|
+
<div class="start-step"><div class="start-num">2</div><div class="start-text">Run <code>npx cc-safe-setup --verify</code> — confirms all hooks block correctly</div></div>
|
|
58
|
+
<div class="start-step"><div class="start-num">3</div><div class="start-text">For teams: <code>npx cc-safe-setup --team</code> — commit <code>.claude/</code> to share with everyone</div></div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div class="section-title">Tools & Documentation</div>
|
|
62
|
+
|
|
63
|
+
<div class="grid">
|
|
64
|
+
<a href="index.html" class="card">
|
|
65
|
+
<div class="card-icon">🔍</div>
|
|
66
|
+
<div class="card-title">Safety Audit</div>
|
|
67
|
+
<div class="card-desc">Score your Claude Code setup out of 100. Find gaps and get fix suggestions.</div>
|
|
68
|
+
<span class="card-tag">interactive</span>
|
|
69
|
+
</a>
|
|
70
|
+
|
|
71
|
+
<a href="hooks-cheatsheet.html" class="card">
|
|
72
|
+
<div class="card-icon">📋</div>
|
|
73
|
+
<div class="card-title">Cheat Sheet</div>
|
|
74
|
+
<div class="card-desc">30+ copy-paste hook patterns. Block, approve, monitor — with one-click copy.</div>
|
|
75
|
+
<span class="card-tag">reference</span>
|
|
76
|
+
</a>
|
|
77
|
+
|
|
78
|
+
<a href="builder.html" class="card">
|
|
79
|
+
<div class="card-icon">🔧</div>
|
|
80
|
+
<div class="card-title">Hook Builder</div>
|
|
81
|
+
<div class="card-desc">Describe what you want in English. Get a working hook + settings.json entry.</div>
|
|
82
|
+
<span class="card-tag">generator</span>
|
|
83
|
+
</a>
|
|
84
|
+
|
|
85
|
+
<a href="by-example.html" class="card">
|
|
86
|
+
<div class="card-icon">⚡</div>
|
|
87
|
+
<div class="card-title">By Example</div>
|
|
88
|
+
<div class="card-desc">10 real GitHub Issues. See the before (disaster) and after (hook saves it).</div>
|
|
89
|
+
<span class="card-tag">learn</span>
|
|
90
|
+
</a>
|
|
91
|
+
|
|
92
|
+
<a href="matrix.html" class="card">
|
|
93
|
+
<div class="card-icon">📊</div>
|
|
94
|
+
<div class="card-title">Hook Matrix</div>
|
|
95
|
+
<div class="card-desc">All hooks in a sortable, filterable table. Find exactly what you need.</div>
|
|
96
|
+
<span class="card-tag">interactive</span>
|
|
97
|
+
</a>
|
|
98
|
+
|
|
99
|
+
<a href="faq.html" class="card">
|
|
100
|
+
<div class="card-icon">❓</div>
|
|
101
|
+
<div class="card-title">FAQ</div>
|
|
102
|
+
<div class="card-desc">15 questions answered. How hooks work, performance, debugging, and more.</div>
|
|
103
|
+
<span class="card-tag">reference</span>
|
|
104
|
+
</a>
|
|
105
|
+
|
|
106
|
+
<a href="settings-reference.html" class="card">
|
|
107
|
+
<div class="card-icon">⚙️</div>
|
|
108
|
+
<div class="card-title">Settings Reference</div>
|
|
109
|
+
<div class="card-desc">Every settings.json field explained. Hooks, permissions, env, models.</div>
|
|
110
|
+
<span class="card-tag">reference</span>
|
|
111
|
+
</a>
|
|
112
|
+
|
|
113
|
+
<a href="troubleshooting.html" class="card">
|
|
114
|
+
<div class="card-icon">🔧</div>
|
|
115
|
+
<div class="card-title">Troubleshooting</div>
|
|
116
|
+
<div class="card-desc">10 common problems with step-by-step fixes. Hook not firing? Start here.</div>
|
|
117
|
+
<span class="card-tag">debug</span>
|
|
118
|
+
</a>
|
|
119
|
+
|
|
120
|
+
<a href="migration-guide.html" class="card">
|
|
121
|
+
<div class="card-icon">🔀</div>
|
|
122
|
+
<div class="card-title">Migration Guide</div>
|
|
123
|
+
<div class="card-desc">Moving from Cursor, Windsurf, Aider, or Copilot? Map your safety setup.</div>
|
|
124
|
+
<span class="card-tag">guide</span>
|
|
125
|
+
</a>
|
|
126
|
+
|
|
127
|
+
<a href="https://yurukusa.github.io/cc-hook-registry/playground.html" class="card">
|
|
128
|
+
<div class="card-icon">🎮</div>
|
|
129
|
+
<div class="card-title">Playground</div>
|
|
130
|
+
<div class="card-desc">Type any command, see which hooks fire. Test before you install.</div>
|
|
131
|
+
<span class="card-tag">interactive</span>
|
|
132
|
+
</a>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<div class="section-title">Also Available</div>
|
|
136
|
+
<div class="grid">
|
|
137
|
+
<a href="https://github.com/yurukusa/cc-safe-setup" class="card">
|
|
138
|
+
<div class="card-title">GitHub Repository</div>
|
|
139
|
+
<div class="card-desc">Source code, issues, releases. Star if useful.</div>
|
|
140
|
+
</a>
|
|
141
|
+
<a href="https://www.npmjs.com/package/cc-safe-setup" class="card">
|
|
142
|
+
<div class="card-title">npm Package</div>
|
|
143
|
+
<div class="card-desc">2,500+ daily downloads. Zero dependencies.</div>
|
|
144
|
+
</a>
|
|
145
|
+
<a href="https://yurukusa.github.io/cc-hook-registry/" class="card">
|
|
146
|
+
<div class="card-title">Hook Registry</div>
|
|
147
|
+
<div class="card-desc">70 hooks from 7 projects. Search, browse, install.</div>
|
|
148
|
+
</a>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<div class="footer">
|
|
152
|
+
cc-safe-setup — MIT License · <a href="https://github.com/yurukusa/cc-safe-setup">GitHub</a> · <a href="https://www.npmjs.com/package/cc-safe-setup">npm</a>
|
|
153
|
+
</div>
|
|
154
|
+
</body>
|
|
155
|
+
</html>
|
package/docs/matrix.html
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Claude Code Hooks Matrix — 112 Hooks at a Glance</title>
|
|
7
|
+
<meta name="description" content="Interactive matrix of all 112 Claude Code hooks. Filter by trigger, category, and action type.">
|
|
8
|
+
<style>
|
|
9
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
10
|
+
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#0d1117;color:#c9d1d9;padding:1rem;font-size:.82rem}
|
|
11
|
+
.c{max-width:1100px;margin:0 auto}
|
|
12
|
+
h1{color:#f0f6fc;font-size:1.3rem;margin-bottom:.2rem}
|
|
13
|
+
.sub{color:#8b949e;font-size:.8rem;margin-bottom:.8rem}
|
|
14
|
+
a{color:#58a6ff;text-decoration:none}
|
|
15
|
+
input{width:100%;padding:.5rem;background:#161b22;border:1px solid #30363d;border-radius:6px;color:#c9d1d9;font-size:.85rem;margin-bottom:.5rem}
|
|
16
|
+
input::placeholder{color:#484f58}
|
|
17
|
+
.filters{display:flex;gap:.3rem;flex-wrap:wrap;margin-bottom:.5rem}
|
|
18
|
+
.f{padding:.2rem .5rem;border-radius:3px;border:1px solid #30363d;background:transparent;color:#8b949e;cursor:pointer;font-size:.7rem}
|
|
19
|
+
.f.on{background:#238636;border-color:#238636;color:#fff}
|
|
20
|
+
table{width:100%;border-collapse:collapse}
|
|
21
|
+
th,td{padding:.3rem .4rem;border:1px solid #21262d;text-align:left;font-size:.75rem}
|
|
22
|
+
th{background:#161b22;color:#f0f6fc;position:sticky;top:0;cursor:pointer}
|
|
23
|
+
th:hover{background:#21262d}
|
|
24
|
+
tr:hover td{background:#161b2288}
|
|
25
|
+
.act-block{color:#f85149;font-weight:bold}
|
|
26
|
+
.act-warn{color:#d29922}
|
|
27
|
+
.act-approve{color:#3fb950}
|
|
28
|
+
.act-monitor{color:#58a6ff}
|
|
29
|
+
.count{color:#8b949e;font-size:.78rem;margin:.3rem 0}
|
|
30
|
+
.footer{text-align:center;color:#484f58;font-size:.65rem;margin-top:1rem}
|
|
31
|
+
td code{background:#21262d;padding:.1rem .2rem;border-radius:2px;font-size:.7rem;cursor:pointer}
|
|
32
|
+
td code:hover{color:#58a6ff}
|
|
33
|
+
</style>
|
|
34
|
+
</head>
|
|
35
|
+
<body>
|
|
36
|
+
<div class="c">
|
|
37
|
+
<h1>Hook Matrix</h1>
|
|
38
|
+
<p class="sub">112 hooks. Click column headers to sort. Search to filter.</p>
|
|
39
|
+
|
|
40
|
+
<input id="q" placeholder="Filter hooks..." oninput="render()">
|
|
41
|
+
<div class="filters" id="filters"></div>
|
|
42
|
+
<div class="count" id="count"></div>
|
|
43
|
+
<div style="overflow-x:auto"><table id="tbl"><thead><tr>
|
|
44
|
+
<th onclick="sortBy('name')">Hook</th>
|
|
45
|
+
<th onclick="sortBy('cat')">Category</th>
|
|
46
|
+
<th onclick="sortBy('trigger')">Trigger</th>
|
|
47
|
+
<th onclick="sortBy('action')">Action</th>
|
|
48
|
+
<th>Description</th>
|
|
49
|
+
<th>Install</th>
|
|
50
|
+
</tr></thead><tbody id="tbody"></tbody></table></div>
|
|
51
|
+
|
|
52
|
+
<div class="footer">
|
|
53
|
+
<a href="hooks-cheatsheet.html">Cheat Sheet</a> ·
|
|
54
|
+
<a href="builder.html">Builder</a> ·
|
|
55
|
+
<a href="troubleshooting.html">Troubleshoot</a> ·
|
|
56
|
+
<a href="https://github.com/yurukusa/cc-safe-setup">GitHub</a>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<script>
|
|
61
|
+
const H=[
|
|
62
|
+
{n:'destructive-guard',c:'safety',t:'Pre',a:'block',d:'rm -rf, git reset --hard, git clean',i:'npx cc-safe-setup'},
|
|
63
|
+
{n:'branch-guard',c:'safety',t:'Pre',a:'block',d:'Push to main/master, force-push',i:'npx cc-safe-setup'},
|
|
64
|
+
{n:'secret-guard',c:'safety',t:'Pre',a:'block',d:'git add .env, credential files',i:'npx cc-safe-setup'},
|
|
65
|
+
{n:'syntax-check',c:'quality',t:'Post',a:'warn',d:'Python/Shell/JSON/JS syntax after edits',i:'npx cc-safe-setup'},
|
|
66
|
+
{n:'context-monitor',c:'monitor',t:'Post',a:'warn',d:'Context warnings at 40/25/20/15%',i:'npx cc-safe-setup'},
|
|
67
|
+
{n:'comment-strip',c:'utility',t:'Pre',a:'warn',d:'Fix bash comments breaking permissions',i:'npx cc-safe-setup'},
|
|
68
|
+
{n:'cd-git-allow',c:'approve',t:'Pre',a:'approve',d:'Auto-approve cd && git compounds',i:'npx cc-safe-setup'},
|
|
69
|
+
{n:'api-error-alert',c:'monitor',t:'Stop',a:'monitor',d:'Notify on session API errors',i:'npx cc-safe-setup'},
|
|
70
|
+
{n:'scope-guard',c:'safety',t:'Pre',a:'block',d:'Block operations outside project',i:'--install-example scope-guard'},
|
|
71
|
+
{n:'no-sudo-guard',c:'safety',t:'Pre',a:'block',d:'Block all sudo commands',i:'--install-example no-sudo-guard'},
|
|
72
|
+
{n:'protect-claudemd',c:'safety',t:'Pre',a:'block',d:'Block edits to CLAUDE.md/settings',i:'--install-example protect-claudemd'},
|
|
73
|
+
{n:'protect-dotfiles',c:'safety',t:'Pre',a:'block',d:'Block .bashrc, .aws/, .ssh/ changes',i:'--install-example protect-dotfiles'},
|
|
74
|
+
{n:'block-database-wipe',c:'safety',t:'Pre',a:'block',d:'DROP DATABASE, migrate:fresh, prisma reset',i:'--install-example block-database-wipe'},
|
|
75
|
+
{n:'deploy-guard',c:'deploy',t:'Pre',a:'warn',d:'Warn deploy with uncommitted changes',i:'--install-example deploy-guard'},
|
|
76
|
+
{n:'no-deploy-friday',c:'deploy',t:'Pre',a:'block',d:'Block deploys on Fridays',i:'--install-example no-deploy-friday'},
|
|
77
|
+
{n:'work-hours-guard',c:'deploy',t:'Pre',a:'warn',d:'Restrict risky ops outside hours',i:'--install-example work-hours-guard'},
|
|
78
|
+
{n:'uncommitted-work-guard',c:'safety',t:'Pre',a:'block',d:'Block destructive git when dirty',i:'--install-example uncommitted-work-guard'},
|
|
79
|
+
{n:'test-deletion-guard',c:'quality',t:'Pre',a:'warn',d:'Warn when removing test assertions',i:'--install-example test-deletion-guard'},
|
|
80
|
+
{n:'overwrite-guard',c:'safety',t:'Pre',a:'warn',d:'Warn before overwriting files',i:'--install-example overwrite-guard'},
|
|
81
|
+
{n:'memory-write-guard',c:'safety',t:'Pre',a:'warn',d:'Log writes to ~/.claude/',i:'--install-example memory-write-guard'},
|
|
82
|
+
{n:'fact-check-gate',c:'quality',t:'Post',a:'warn',d:'Docs reference unread source files',i:'--install-example fact-check-gate'},
|
|
83
|
+
{n:'token-budget-guard',c:'monitor',t:'Post',a:'block',d:'Block when cost exceeds budget',i:'--install-example token-budget-guard'},
|
|
84
|
+
{n:'conflict-marker-guard',c:'quality',t:'Pre',a:'block',d:'Block commits with conflict markers',i:'--install-example conflict-marker-guard'},
|
|
85
|
+
{n:'error-memory-guard',c:'safety',t:'Post',a:'block',d:'Block retries of failed commands',i:'--install-example error-memory-guard'},
|
|
86
|
+
{n:'strict-allowlist',c:'safety',t:'Pre',a:'block',d:'Only allow permitted commands',i:'--install-example strict-allowlist'},
|
|
87
|
+
{n:'hardcoded-secret-detector',c:'security',t:'Post',a:'warn',d:'Detect AWS keys, passwords, JWT',i:'--install-example hardcoded-secret-detector'},
|
|
88
|
+
{n:'auto-approve-build',c:'approve',t:'Pre',a:'approve',d:'npm test, cargo build, go test',i:'--install-example auto-approve-build'},
|
|
89
|
+
{n:'auto-approve-python',c:'approve',t:'Pre',a:'approve',d:'pytest, mypy, ruff, black',i:'--install-example auto-approve-python'},
|
|
90
|
+
{n:'auto-approve-docker',c:'approve',t:'Pre',a:'approve',d:'docker build, compose, ps',i:'--install-example auto-approve-docker'},
|
|
91
|
+
{n:'auto-approve-go',c:'approve',t:'Pre',a:'approve',d:'go build/test/vet/fmt',i:'--install-example auto-approve-go'},
|
|
92
|
+
{n:'auto-approve-cargo',c:'approve',t:'Pre',a:'approve',d:'cargo build/test/clippy',i:'--install-example auto-approve-cargo'},
|
|
93
|
+
{n:'auto-approve-make',c:'approve',t:'Pre',a:'approve',d:'make build/test/lint',i:'--install-example auto-approve-make'},
|
|
94
|
+
{n:'auto-approve-gradle',c:'approve',t:'Pre',a:'approve',d:'gradle build/test',i:'--install-example auto-approve-gradle'},
|
|
95
|
+
{n:'auto-approve-maven',c:'approve',t:'Pre',a:'approve',d:'mvn compile/test/verify',i:'--install-example auto-approve-maven'},
|
|
96
|
+
{n:'loop-detector',c:'safety',t:'Pre',a:'block',d:'Break command repetition loops',i:'--install-example loop-detector'},
|
|
97
|
+
{n:'cost-tracker',c:'monitor',t:'Post',a:'monitor',d:'Estimate session token cost',i:'--install-example cost-tracker'},
|
|
98
|
+
{n:'session-handoff',c:'utility',t:'Stop',a:'monitor',d:'Save state for next session',i:'--install-example session-handoff'},
|
|
99
|
+
{n:'revert-helper',c:'utility',t:'Stop',a:'monitor',d:'Show undo on session end',i:'--install-example revert-helper'},
|
|
100
|
+
{n:'compact-reminder',c:'utility',t:'Stop',a:'warn',d:'Suggest /compact after N calls',i:'--install-example compact-reminder'},
|
|
101
|
+
{n:'env-drift-guard',c:'quality',t:'Post',a:'warn',d:'.env vs .env.example mismatch',i:'--install-example env-drift-guard'},
|
|
102
|
+
{n:'package-script-guard',c:'quality',t:'Pre',a:'warn',d:'Warn on package.json script changes',i:'--install-example package-script-guard'},
|
|
103
|
+
{n:'git-blame-context',c:'quality',t:'Pre',a:'monitor',d:'Show file ownership before edits',i:'--install-example git-blame-context'},
|
|
104
|
+
{n:'import-cycle-warn',c:'quality',t:'Post',a:'warn',d:'Detect circular imports',i:'--install-example import-cycle-warn'},
|
|
105
|
+
{n:'lockfile-guard',c:'quality',t:'Pre',a:'warn',d:'Warn when lockfiles modified',i:'--install-example lockfile-guard'},
|
|
106
|
+
{n:'context-snapshot',c:'utility',t:'Stop',a:'monitor',d:'Save branch/files/TODOs state',i:'--install-example context-snapshot'},
|
|
107
|
+
{n:'prompt-injection-guard',c:'security',t:'Post',a:'warn',d:'Detect injection in output',i:'--install-example prompt-injection-guard'},
|
|
108
|
+
{n:'no-eval',c:'security',t:'Post',a:'warn',d:'Warn on eval() usage',i:'--install-example no-eval'},
|
|
109
|
+
{n:'no-console-log',c:'quality',t:'Post',a:'warn',d:'Warn on console.log in prod',i:'--install-example no-console-log'},
|
|
110
|
+
{n:'no-wildcard-import',c:'quality',t:'Post',a:'warn',d:'Warn on import * patterns',i:'--install-example no-wildcard-import'},
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
const cats=[...new Set(H.map(h=>h.c))].sort();
|
|
114
|
+
const actClass={block:'act-block',warn:'act-warn',approve:'act-approve',monitor:'act-monitor'};
|
|
115
|
+
let filter='all',sortKey='name',sortDir=1;
|
|
116
|
+
|
|
117
|
+
document.getElementById('filters').innerHTML=['all',...cats].map(c=>`<button class="f${c==='all'?' on':''}" onclick="setF('${c}')">${c}</button>`).join('');
|
|
118
|
+
|
|
119
|
+
function setF(f){filter=f;document.querySelectorAll('.f').forEach(b=>b.classList.toggle('on',b.textContent===f));render();}
|
|
120
|
+
function sortBy(k){if(sortKey===k)sortDir*=-1;else{sortKey=k;sortDir=1;}render();}
|
|
121
|
+
|
|
122
|
+
function render(){
|
|
123
|
+
const q=document.getElementById('q').value.toLowerCase();
|
|
124
|
+
let list=H.filter(h=>(filter==='all'||h.c===filter)&&(!q||h.n.includes(q)||h.d.toLowerCase().includes(q)||h.c.includes(q)));
|
|
125
|
+
list.sort((a,b)=>{const av=a[sortKey]||'',bv=b[sortKey]||'';return av<bv?-sortDir:av>bv?sortDir:0;});
|
|
126
|
+
document.getElementById('count').textContent=list.length+'/'+H.length+' hooks';
|
|
127
|
+
document.getElementById('tbody').innerHTML=list.map(h=>`<tr>
|
|
128
|
+
<td><strong>${h.n}</strong></td>
|
|
129
|
+
<td>${h.c}</td>
|
|
130
|
+
<td>${h.t}</td>
|
|
131
|
+
<td class="${actClass[h.a]||''}">${h.a}</td>
|
|
132
|
+
<td>${h.d}</td>
|
|
133
|
+
<td><code onclick="navigator.clipboard.writeText('npx cc-safe-setup ${h.i}');this.textContent='✓'">${h.i.substring(0,25)}</code></td>
|
|
134
|
+
</tr>`).join('');
|
|
135
|
+
}
|
|
136
|
+
render();
|
|
137
|
+
</script>
|
|
138
|
+
</body>
|
|
139
|
+
</html>
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Claude Code Troubleshooting — Fix Any Problem in 2 Minutes</title>
|
|
7
|
+
<meta name="description" content="Hook not firing? Claude ignoring rules? Permission prompt spam? Fix every common Claude Code problem.">
|
|
8
|
+
<style>
|
|
9
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
10
|
+
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#0d1117;color:#c9d1d9;padding:1.5rem;line-height:1.6}
|
|
11
|
+
.c{max-width:800px;margin:0 auto}
|
|
12
|
+
h1{color:#f0f6fc;font-size:1.4rem;margin-bottom:.3rem}
|
|
13
|
+
h2{color:#f0f6fc;font-size:1rem;margin:1.2rem 0 .4rem}
|
|
14
|
+
.sub{color:#8b949e;font-size:.83rem;margin-bottom:1rem}
|
|
15
|
+
a{color:#58a6ff;text-decoration:none}
|
|
16
|
+
code{background:#161b22;padding:.12rem .25rem;border-radius:3px;font-size:.82rem}
|
|
17
|
+
pre{background:#161b22;border:1px solid #30363d;border-radius:6px;padding:.5rem;font-size:.78rem;color:#e6edf3;overflow-x:auto;margin:.3rem 0}
|
|
18
|
+
.problem{background:#161b22;border:1px solid #30363d;border-radius:8px;margin:.6rem 0;overflow:hidden}
|
|
19
|
+
.problem-header{padding:.6rem .8rem;cursor:pointer;font-weight:600;color:#f0f6fc;font-size:.88rem;display:flex;align-items:center;gap:.5rem}
|
|
20
|
+
.problem-header:hover{background:#21262d}
|
|
21
|
+
.problem-icon{font-size:1rem}
|
|
22
|
+
.problem-body{padding:0 .8rem .6rem;font-size:.82rem;display:none}
|
|
23
|
+
.problem.open .problem-body{display:block}
|
|
24
|
+
.step{background:#0d1117;border-radius:4px;padding:.4rem .6rem;margin:.3rem 0;border-left:3px solid #30363d}
|
|
25
|
+
.step-yes{border-left-color:#238636}
|
|
26
|
+
.step-no{border-left-color:#da3633}
|
|
27
|
+
.step-fix{border-left-color:#58a6ff}
|
|
28
|
+
.footer{text-align:center;color:#484f58;font-size:.7rem;margin-top:2rem;padding-top:1rem;border-top:1px solid #21262d}
|
|
29
|
+
.quick{background:#161b22;border:1px solid #30363d;border-radius:6px;padding:.6rem;margin:.5rem 0;font-size:.82rem}
|
|
30
|
+
</style>
|
|
31
|
+
</head>
|
|
32
|
+
<body>
|
|
33
|
+
<div class="c">
|
|
34
|
+
|
|
35
|
+
<h1>Troubleshooting</h1>
|
|
36
|
+
<p class="sub">Click your problem. Follow the steps. Fixed in 2 minutes.</p>
|
|
37
|
+
|
|
38
|
+
<div class="quick">
|
|
39
|
+
<strong>Quick fix for most problems:</strong> <code>npx cc-safe-setup --quickfix</code>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div class="problem" onclick="this.classList.toggle('open')">
|
|
43
|
+
<div class="problem-header"><span class="problem-icon">🔇</span> My hook doesn't fire</div>
|
|
44
|
+
<div class="problem-body">
|
|
45
|
+
<div class="step">1. Is the hook file executable? <code>ls -la ~/.claude/hooks/your-hook.sh</code></div>
|
|
46
|
+
<div class="step-fix">Fix: <code>chmod +x ~/.claude/hooks/your-hook.sh</code></div>
|
|
47
|
+
|
|
48
|
+
<div class="step">2. Does it have a shebang? First line must be <code>#!/bin/bash</code></div>
|
|
49
|
+
<div class="step-fix">Fix: Add <code>#!/bin/bash</code> as the very first line</div>
|
|
50
|
+
|
|
51
|
+
<div class="step">3. Is it registered in settings.json?</div>
|
|
52
|
+
<pre>cat ~/.claude/settings.json | python3 -m json.tool | grep your-hook</pre>
|
|
53
|
+
<div class="step-fix">Fix: <code>npx cc-safe-setup --shield</code> (auto-registers all hooks)</div>
|
|
54
|
+
|
|
55
|
+
<div class="step">4. Is the matcher correct? <code>"Bash"</code> won't fire for Edit/Write</div>
|
|
56
|
+
<div class="step-fix">Use <code>""</code> (empty) to match all tools, or <code>"Edit|Write"</code> for file ops</div>
|
|
57
|
+
|
|
58
|
+
<div class="step">5. Is jq installed? <code>which jq</code></div>
|
|
59
|
+
<div class="step-fix">Install: <code>brew install jq</code> (macOS) or <code>sudo apt install jq</code> (Linux)</div>
|
|
60
|
+
|
|
61
|
+
<div class="step">6. Test it manually:</div>
|
|
62
|
+
<pre>echo '{"tool_input":{"command":"rm -rf /"}}' | bash ~/.claude/hooks/your-hook.sh; echo "Exit: $?"</pre>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div class="problem" onclick="this.classList.toggle('open')">
|
|
67
|
+
<div class="problem-header"><span class="problem-icon">🚫</span> My hook blocks everything</div>
|
|
68
|
+
<div class="problem-body">
|
|
69
|
+
<div class="step">Your regex pattern is too broad. Test with a safe command:</div>
|
|
70
|
+
<pre>echo '{"tool_input":{"command":"ls -la"}}' | bash ~/.claude/hooks/your-hook.sh; echo "Exit: $?"</pre>
|
|
71
|
+
<div class="step">If it exits 2 for <code>ls</code>, your grep pattern matches too much.</div>
|
|
72
|
+
<div class="step-fix">Check the <code>grep -qE</code> pattern in your hook. Add <code>\b</code> word boundaries.</div>
|
|
73
|
+
<div class="step-fix">Example: <code>grep -qE '\brm\s+.*-rf\s+/'</code> instead of <code>grep -q 'rm'</code></div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div class="problem" onclick="this.classList.toggle('open')">
|
|
78
|
+
<div class="problem-header"><span class="problem-icon">📋</span> Claude ignores CLAUDE.md rules</div>
|
|
79
|
+
<div class="problem-body">
|
|
80
|
+
<div class="step">This is normal — CLAUDE.md rules degrade as context fills up.</div>
|
|
81
|
+
<div class="step-fix"><strong>Solution: Convert critical rules to hooks.</strong></div>
|
|
82
|
+
<pre># Example: "Don't push to main" as a hook instead of a rule
|
|
83
|
+
npx cc-safe-setup # Installs branch-guard</pre>
|
|
84
|
+
<div class="step">For any rule that must be enforced 100%, create a hook:</div>
|
|
85
|
+
<pre>npx cc-safe-setup --create "your rule in plain English"</pre>
|
|
86
|
+
<div class="step-fix">CLAUDE.md is for guidelines. Hooks are for hard constraints. Use both.</div>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div class="problem" onclick="this.classList.toggle('open')">
|
|
91
|
+
<div class="problem-header"><span class="problem-icon">🔔</span> Too many permission prompts</div>
|
|
92
|
+
<div class="problem-body">
|
|
93
|
+
<div class="step">Install auto-approve hooks for safe commands:</div>
|
|
94
|
+
<pre>npx cc-safe-setup --install-example auto-approve-build # npm test, cargo build
|
|
95
|
+
npx cc-safe-setup --install-example auto-approve-python # pytest, mypy
|
|
96
|
+
npx cc-safe-setup --install-example auto-approve-git-read # git status, git log
|
|
97
|
+
npx cc-safe-setup --install-example compound-command-approver # cd && git log</pre>
|
|
98
|
+
<div class="step">Or add permissions in settings.json:</div>
|
|
99
|
+
<pre>"permissions": {"allow": ["Bash(npm test)", "Bash(git status)", "Read(*)"]}</pre>
|
|
100
|
+
<div class="step-fix">The <code>--profile standard</code> includes auto-approve hooks for common commands.</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<div class="problem" onclick="this.classList.toggle('open')">
|
|
105
|
+
<div class="problem-header"><span class="problem-icon">💾</span> settings.json won't parse</div>
|
|
106
|
+
<div class="problem-body">
|
|
107
|
+
<div class="step">Validate:</div>
|
|
108
|
+
<pre>python3 -c "import json; json.load(open('$HOME/.claude/settings.json'))"</pre>
|
|
109
|
+
<div class="step-fix">Common causes: trailing commas, comments (JSON doesn't allow them), unescaped quotes</div>
|
|
110
|
+
<div class="step">Auto-fix:</div>
|
|
111
|
+
<pre>npx cc-safe-setup --quickfix</pre>
|
|
112
|
+
<div class="step-fix">If totally broken: <code>echo '{}' > ~/.claude/settings.json</code> then re-run setup</div>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<div class="problem" onclick="this.classList.toggle('open')">
|
|
117
|
+
<div class="problem-header"><span class="problem-icon">🔄</span> Claude keeps retrying the same failed command</div>
|
|
118
|
+
<div class="problem-body">
|
|
119
|
+
<div class="step">Install the error memory guard:</div>
|
|
120
|
+
<pre>npx cc-safe-setup --install-example error-memory-guard</pre>
|
|
121
|
+
<div class="step-fix">After 3 failures of the same command, it blocks and says "try a different approach".</div>
|
|
122
|
+
<div class="step">Also useful: loop detector</div>
|
|
123
|
+
<pre>npx cc-safe-setup --install-example loop-detector</pre>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<div class="problem" onclick="this.classList.toggle('open')">
|
|
128
|
+
<div class="problem-header"><span class="problem-icon">💸</span> Session costs too much</div>
|
|
129
|
+
<div class="problem-body">
|
|
130
|
+
<div class="step">Install token budget guard:</div>
|
|
131
|
+
<pre>npx cc-safe-setup --install-example token-budget-guard</pre>
|
|
132
|
+
<div class="step-fix">Warns at $10, blocks at $50 (configurable via <code>CC_TOKEN_BUDGET</code> and <code>CC_TOKEN_BLOCK</code>)</div>
|
|
133
|
+
<div class="step">Monitor with:</div>
|
|
134
|
+
<pre>npx cc-safe-setup --analyze</pre>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<div class="problem" onclick="this.classList.toggle('open')">
|
|
139
|
+
<div class="problem-header"><span class="problem-icon">🧠</span> Context fills up too fast</div>
|
|
140
|
+
<div class="problem-body">
|
|
141
|
+
<div class="step">Three hooks help:</div>
|
|
142
|
+
<pre>npx cc-safe-setup --install-example output-length-guard # Warn on large outputs
|
|
143
|
+
npx cc-safe-setup --install-example large-read-guard # Warn before cat on large files
|
|
144
|
+
npx cc-safe-setup --install-example compact-reminder # Suggest /compact after N calls</pre>
|
|
145
|
+
<div class="step-fix">The context-monitor hook (installed by default) warns at 40%, 25%, 20%, 15%.</div>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<div class="problem" onclick="this.classList.toggle('open')">
|
|
150
|
+
<div class="problem-header"><span class="problem-icon">👥</span> Team members have different hook setups</div>
|
|
151
|
+
<div class="problem-body">
|
|
152
|
+
<div class="step">Use project-level hooks:</div>
|
|
153
|
+
<pre>npx cc-safe-setup --team
|
|
154
|
+
git add .claude/
|
|
155
|
+
git commit -m "chore: add team safety hooks"</pre>
|
|
156
|
+
<div class="step-fix">Hooks are copied to <code>.claude/hooks/</code> with relative paths. Works on any machine.</div>
|
|
157
|
+
<div class="step">Generate CI to enforce:</div>
|
|
158
|
+
<pre>npx cc-safe-setup --generate-ci</pre>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<div class="problem" onclick="this.classList.toggle('open')">
|
|
163
|
+
<div class="problem-header"><span class="problem-icon">🔀</span> Hooks from different tools conflict</div>
|
|
164
|
+
<div class="problem-body">
|
|
165
|
+
<div class="step">Analyze what you have:</div>
|
|
166
|
+
<pre>npx cc-safe-setup --migrate-from manual # See all existing hooks
|
|
167
|
+
npx cc-safe-setup --health # Check hook health</pre>
|
|
168
|
+
<div class="step-fix">Hooks in the same group run sequentially. If one consumes stdin, the next gets nothing.</div>
|
|
169
|
+
<div class="step">Fix: Put each hook in a separate matcher group in settings.json.</div>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<div class="quick" style="margin-top:1rem">
|
|
174
|
+
<strong>Still stuck?</strong>
|
|
175
|
+
<code>npx cc-safe-setup --doctor</code> — full diagnosis<br>
|
|
176
|
+
<code>npx cc-safe-setup --quickfix</code> — auto-fix common issues<br>
|
|
177
|
+
<a href="faq.html">FAQ</a> — 15 questions answered
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<div class="footer">
|
|
181
|
+
<a href="hooks-cheatsheet.html">Cheat Sheet</a> ·
|
|
182
|
+
<a href="builder.html">Builder</a> ·
|
|
183
|
+
<a href="faq.html">FAQ</a> ·
|
|
184
|
+
<a href="settings-reference.html">Settings Ref</a> ·
|
|
185
|
+
<a href="https://github.com/yurukusa/cc-safe-setup">GitHub</a>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
</body>
|
|
189
|
+
</html>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// destructive_guard.rs — Claude Code PreToolUse hook in Rust
|
|
2
|
+
//
|
|
3
|
+
// Blocks rm -rf /, git reset --hard, git clean -fd, and similar
|
|
4
|
+
// destructive commands. Exit code 2 = block, 0 = allow.
|
|
5
|
+
//
|
|
6
|
+
// Build: rustc destructive_guard.rs -o destructive-guard
|
|
7
|
+
// Usage: {"type": "command", "command": "/path/to/destructive-guard"}
|
|
8
|
+
|
|
9
|
+
use std::io::{self, Read};
|
|
10
|
+
use std::process;
|
|
11
|
+
|
|
12
|
+
fn main() {
|
|
13
|
+
let mut input = String::new();
|
|
14
|
+
if io::stdin().read_to_string(&mut input).is_err() {
|
|
15
|
+
process::exit(0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Simple JSON parsing without serde (zero dependencies)
|
|
19
|
+
let cmd = extract_command(&input);
|
|
20
|
+
if cmd.is_empty() {
|
|
21
|
+
process::exit(0);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Skip echo/printf context
|
|
25
|
+
let trimmed = cmd.trim_start().to_lowercase();
|
|
26
|
+
if trimmed.starts_with("echo ") || trimmed.starts_with("printf ") {
|
|
27
|
+
process::exit(0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let patterns: &[&str] = &[
|
|
31
|
+
"rm -rf /",
|
|
32
|
+
"rm -rf ~/",
|
|
33
|
+
"rm -rf ../",
|
|
34
|
+
"rm -rf .",
|
|
35
|
+
"git reset --hard",
|
|
36
|
+
"git clean -f",
|
|
37
|
+
"git checkout --force",
|
|
38
|
+
"chmod 777 /",
|
|
39
|
+
"find / -delete",
|
|
40
|
+
"--no-preserve-root",
|
|
41
|
+
"sudo mkfs",
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
for pattern in patterns {
|
|
45
|
+
if cmd.to_lowercase().contains(&pattern.to_lowercase()) {
|
|
46
|
+
eprintln!("BLOCKED: Dangerous command detected");
|
|
47
|
+
eprintln!("Command: {}", cmd);
|
|
48
|
+
process::exit(2);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
process::exit(0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
fn extract_command(json: &str) -> String {
|
|
56
|
+
// Extract .tool_input.command from JSON without a parser
|
|
57
|
+
if let Some(pos) = json.find("\"command\"") {
|
|
58
|
+
let rest = &json[pos + 9..];
|
|
59
|
+
if let Some(start) = rest.find('"') {
|
|
60
|
+
let value_start = start + 1;
|
|
61
|
+
let mut end = value_start;
|
|
62
|
+
let bytes = rest.as_bytes();
|
|
63
|
+
while end < bytes.len() {
|
|
64
|
+
if bytes[end] == b'"' && (end == 0 || bytes[end - 1] != b'\\') {
|
|
65
|
+
return rest[value_start..end].replace("\\\"", "\"").replace("\\\\", "\\");
|
|
66
|
+
}
|
|
67
|
+
end += 1;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
String::new()
|
|
72
|
+
}
|
package/index.mjs
CHANGED
|
@@ -96,6 +96,7 @@ const TEAM = process.argv.includes('--team');
|
|
|
96
96
|
const MIGRATE_FROM_IDX = process.argv.findIndex(a => a === '--migrate-from');
|
|
97
97
|
const MIGRATE_FROM = MIGRATE_FROM_IDX !== -1 ? process.argv[MIGRATE_FROM_IDX + 1] : null;
|
|
98
98
|
const HEALTH = process.argv.includes('--health');
|
|
99
|
+
const FROM_CLAUDEMD = process.argv.includes('--from-claudemd');
|
|
99
100
|
const PROFILE_IDX = process.argv.findIndex(a => a === '--profile');
|
|
100
101
|
const PROFILE = PROFILE_IDX !== -1 ? process.argv[PROFILE_IDX + 1] : null;
|
|
101
102
|
const COMPARE_IDX = process.argv.findIndex(a => a === '--compare');
|
|
@@ -133,6 +134,7 @@ if (HELP) {
|
|
|
133
134
|
npx cc-safe-setup --doctor Diagnose why hooks aren't working
|
|
134
135
|
npx cc-safe-setup --watch Live dashboard of blocked commands
|
|
135
136
|
npx cc-safe-setup --create "<desc>" Generate a custom hook from description
|
|
137
|
+
npx cc-safe-setup --from-claudemd Convert CLAUDE.md rules into hooks
|
|
136
138
|
npx cc-safe-setup --health Hook health dashboard (size, permissions, age)
|
|
137
139
|
npx cc-safe-setup --migrate-from <tool> Migrate from safety-net/hooks-mastery/etc.
|
|
138
140
|
npx cc-safe-setup --team Set up project-level hooks (commit to repo for team)
|
|
@@ -845,6 +847,89 @@ async function fullSetup() {
|
|
|
845
847
|
console.log();
|
|
846
848
|
}
|
|
847
849
|
|
|
850
|
+
async function fromClaudeMd() {
|
|
851
|
+
console.log();
|
|
852
|
+
console.log(c.bold + ' cc-safe-setup --from-claudemd' + c.reset);
|
|
853
|
+
console.log(c.dim + ' Convert CLAUDE.md rules into enforceable hooks' + c.reset);
|
|
854
|
+
console.log();
|
|
855
|
+
|
|
856
|
+
// Find CLAUDE.md
|
|
857
|
+
const cwd = process.cwd();
|
|
858
|
+
let claudeMdPath = null;
|
|
859
|
+
for (const p of [join(cwd, 'CLAUDE.md'), join(cwd, '.claude', 'CLAUDE.md')]) {
|
|
860
|
+
if (existsSync(p)) { claudeMdPath = p; break; }
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
if (!claudeMdPath) {
|
|
864
|
+
console.log(c.yellow + ' No CLAUDE.md found in project.' + c.reset);
|
|
865
|
+
console.log(c.dim + ' Run: npx cc-safe-setup --shield (creates one)' + c.reset);
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
const content = readFileSync(claudeMdPath, 'utf-8').toLowerCase();
|
|
870
|
+
console.log(c.dim + ' Reading: ' + claudeMdPath + c.reset);
|
|
871
|
+
console.log();
|
|
872
|
+
|
|
873
|
+
// Pattern matching: CLAUDE.md rules → hooks
|
|
874
|
+
const RULE_MAP = [
|
|
875
|
+
{ patterns: ['do not push to main', 'don\'t push to main', 'no push main', 'never push to main'], hook: 'branch-guard', desc: '"Do not push to main" → branch-guard' },
|
|
876
|
+
{ patterns: ['do not force', 'no force push', 'don\'t force push'], hook: 'branch-guard', desc: '"No force push" → branch-guard' },
|
|
877
|
+
{ patterns: ['do not delete', 'don\'t delete', 'no rm -rf', 'never delete'], hook: 'destructive-guard', desc: '"Do not delete files" → destructive-guard' },
|
|
878
|
+
{ patterns: ['do not commit .env', 'don\'t commit secret', 'no credentials', 'never commit .env'], hook: 'secret-guard', desc: '"No .env commits" → secret-guard' },
|
|
879
|
+
{ patterns: ['run tests before', 'test before commit', 'tests must pass'], hook: 'verify-before-done', desc: '"Run tests first" → verify-before-done' },
|
|
880
|
+
{ patterns: ['do not use sudo', 'no sudo', 'don\'t use sudo'], hook: 'no-sudo-guard', desc: '"No sudo" → no-sudo-guard' },
|
|
881
|
+
{ patterns: ['stay in project', 'only this project', 'don\'t modify outside', 'do not edit files outside'], hook: 'scope-guard', desc: '"Stay in project" → scope-guard' },
|
|
882
|
+
{ patterns: ['don\'t modify .bashrc', 'do not edit dotfile', 'protect home'], hook: 'protect-dotfiles', desc: '"Protect dotfiles" → protect-dotfiles' },
|
|
883
|
+
{ patterns: ['do not deploy on friday', 'no friday deploy'], hook: 'no-deploy-friday', desc: '"No Friday deploy" → no-deploy-friday' },
|
|
884
|
+
{ patterns: ['do not install global', 'no npm -g', 'don\'t install globally'], hook: 'no-install-global', desc: '"No global installs" → no-install-global' },
|
|
885
|
+
{ patterns: ['descriptive commit', 'meaningful commit', 'good commit message'], hook: 'commit-quality-gate', desc: '"Good commit messages" → commit-quality-gate' },
|
|
886
|
+
{ patterns: ['one logical change', 'small commit', 'focused commit', 'don\'t commit too many'], hook: 'commit-scope-guard', desc: '"Small commits" → commit-scope-guard' },
|
|
887
|
+
{ patterns: ['feature branch', 'create branch', 'feat/ fix/ chore/'], hook: 'branch-naming-convention', desc: '"Feature branches" → branch-naming-convention' },
|
|
888
|
+
{ patterns: ['do not drop database', 'no migrate:fresh', 'protect database'], hook: 'block-database-wipe', desc: '"Protect database" → block-database-wipe' },
|
|
889
|
+
{ patterns: ['read before edit', 'understand before changing'], hook: 'read-before-edit', desc: '"Read first" → read-before-edit' },
|
|
890
|
+
{ patterns: ['do not overwrite', 'use edit not write'], hook: 'overwrite-guard', desc: '"Use Edit, not Write" → overwrite-guard' },
|
|
891
|
+
];
|
|
892
|
+
|
|
893
|
+
const matched = [];
|
|
894
|
+
for (const rule of RULE_MAP) {
|
|
895
|
+
if (rule.patterns.some(p => content.includes(p))) {
|
|
896
|
+
matched.push(rule);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
if (matched.length === 0) {
|
|
901
|
+
console.log(c.yellow + ' No enforceable rules detected in CLAUDE.md.' + c.reset);
|
|
902
|
+
console.log(c.dim + ' CLAUDE.md may contain guidelines that can\'t be converted to hooks.' + c.reset);
|
|
903
|
+
console.log(c.dim + ' For custom hooks: npx cc-safe-setup --create "your rule"' + c.reset);
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
console.log(c.bold + ` Found ${matched.length} rules that can be enforced with hooks:` + c.reset);
|
|
908
|
+
console.log();
|
|
909
|
+
|
|
910
|
+
for (const m of matched) {
|
|
911
|
+
const hookPath = join(HOOKS_DIR, `${m.hook}.sh`);
|
|
912
|
+
const installed = existsSync(hookPath);
|
|
913
|
+
const icon = installed ? c.green + '✓' + c.reset : c.yellow + '○' + c.reset;
|
|
914
|
+
const status = installed ? c.dim + '(installed)' + c.reset : '';
|
|
915
|
+
console.log(` ${icon} ${m.desc} ${status}`);
|
|
916
|
+
if (!installed) {
|
|
917
|
+
console.log(c.dim + ` npx cc-safe-setup --install-example ${m.hook}` + c.reset);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
const notInstalled = matched.filter(m => !existsSync(join(HOOKS_DIR, `${m.hook}.sh`)));
|
|
922
|
+
if (notInstalled.length > 0) {
|
|
923
|
+
console.log();
|
|
924
|
+
console.log(c.bold + ` Install all ${notInstalled.length} missing hooks:` + c.reset);
|
|
925
|
+
console.log(c.dim + ' npx cc-safe-setup --shield' + c.reset);
|
|
926
|
+
} else {
|
|
927
|
+
console.log();
|
|
928
|
+
console.log(c.green + ' All detected rules are already enforced by hooks!' + c.reset);
|
|
929
|
+
}
|
|
930
|
+
console.log();
|
|
931
|
+
}
|
|
932
|
+
|
|
848
933
|
async function health() {
|
|
849
934
|
const { readdirSync, statSync } = await import('fs');
|
|
850
935
|
console.log();
|
|
@@ -3638,6 +3723,7 @@ async function main() {
|
|
|
3638
3723
|
if (FULL) return fullSetup();
|
|
3639
3724
|
if (DOCTOR) return doctor();
|
|
3640
3725
|
if (WATCH) return watch();
|
|
3726
|
+
if (FROM_CLAUDEMD) return fromClaudeMd();
|
|
3641
3727
|
if (HEALTH) return health();
|
|
3642
3728
|
if (MIGRATE_FROM_IDX !== -1) return migrateFrom(MIGRATE_FROM);
|
|
3643
3729
|
if (TEAM) return team();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-safe-setup",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.3.0",
|
|
4
4
|
"description": "One command to make Claude Code safe. 59 hooks (8 built-in + 51 examples). 26 CLI commands: dashboard, create, audit, lint, diff, migrate, compare, generate-ci. 284 tests.",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"bin": {
|