cc-safe-setup 10.5.0 → 10.6.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/index.mjs +80 -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 = **118 hooks**.
|
|
9
|
+
8 built-in + 104 examples = **118 hooks**. 38 CLI commands. 531 tests. 5 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/index.mjs
CHANGED
|
@@ -105,6 +105,7 @@ const PROFILE_IDX = process.argv.findIndex(a => a === '--profile');
|
|
|
105
105
|
const PROFILE = PROFILE_IDX !== -1 ? process.argv[PROFILE_IDX + 1] : null;
|
|
106
106
|
const COMPARE_IDX = process.argv.findIndex(a => a === '--compare');
|
|
107
107
|
const COMPARE = COMPARE_IDX !== -1 ? { a: process.argv[COMPARE_IDX + 1], b: process.argv[COMPARE_IDX + 2] } : null;
|
|
108
|
+
const REPLAY = process.argv.includes('--replay');
|
|
108
109
|
const CREATE_IDX = process.argv.findIndex(a => a === '--create');
|
|
109
110
|
const CREATE_DESC = CREATE_IDX !== -1 ? process.argv.slice(CREATE_IDX + 1).join(' ') : null;
|
|
110
111
|
|
|
@@ -138,6 +139,7 @@ if (HELP) {
|
|
|
138
139
|
npx cc-safe-setup --doctor Diagnose why hooks aren't working
|
|
139
140
|
npx cc-safe-setup --watch Live dashboard of blocked commands
|
|
140
141
|
npx cc-safe-setup --create "<desc>" Generate a custom hook from description
|
|
142
|
+
npx cc-safe-setup --replay Replay blocked commands timeline (demo/review)
|
|
141
143
|
npx cc-safe-setup --guard "<rule>" Instantly enforce a rule (generate + install + activate)
|
|
142
144
|
npx cc-safe-setup --diff-hooks <path> Compare hooks between two settings files
|
|
143
145
|
npx cc-safe-setup --from-claudemd Convert CLAUDE.md rules into hooks
|
|
@@ -853,6 +855,83 @@ async function fullSetup() {
|
|
|
853
855
|
console.log();
|
|
854
856
|
}
|
|
855
857
|
|
|
858
|
+
async function replay() {
|
|
859
|
+
console.log();
|
|
860
|
+
console.log(c.bold + ' cc-safe-setup --replay' + c.reset);
|
|
861
|
+
console.log(c.dim + ' Replay blocked commands timeline' + c.reset);
|
|
862
|
+
console.log();
|
|
863
|
+
|
|
864
|
+
const LOG_PATH = join(HOME, '.claude', 'blocked-commands.log');
|
|
865
|
+
if (!existsSync(LOG_PATH)) {
|
|
866
|
+
console.log(c.dim + ' No blocked commands log found.' + c.reset);
|
|
867
|
+
console.log(c.dim + ' Hooks will create it when they block something.' + c.reset);
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const content = readFileSync(LOG_PATH, 'utf-8');
|
|
872
|
+
const lines = content.split('\n').filter(l => l.trim());
|
|
873
|
+
|
|
874
|
+
if (lines.length === 0) {
|
|
875
|
+
console.log(c.dim + ' Log is empty — no commands blocked yet.' + c.reset);
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// Parse entries
|
|
880
|
+
const entries = [];
|
|
881
|
+
for (const line of lines) {
|
|
882
|
+
const match = line.match(/^\[([^\]]+)\]\s*BLOCKED:\s*(.+?)\s*\|\s*cmd:\s*(.+)$/);
|
|
883
|
+
if (match) {
|
|
884
|
+
entries.push({ time: match[1], reason: match[2].trim(), cmd: match[3].trim() });
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Group by day
|
|
889
|
+
const days = {};
|
|
890
|
+
for (const e of entries) {
|
|
891
|
+
const day = e.time.split('T')[0] || 'unknown';
|
|
892
|
+
if (!days[day]) days[day] = [];
|
|
893
|
+
days[day].push(e);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// Show last 7 days
|
|
897
|
+
const sortedDays = Object.keys(days).sort().slice(-7);
|
|
898
|
+
|
|
899
|
+
for (const day of sortedDays) {
|
|
900
|
+
const dayEntries = days[day];
|
|
901
|
+
console.log(c.bold + ` ${day}` + c.reset + c.dim + ` (${dayEntries.length} blocks)` + c.reset);
|
|
902
|
+
|
|
903
|
+
// Category counts
|
|
904
|
+
const cats = {};
|
|
905
|
+
for (const e of dayEntries) {
|
|
906
|
+
const cat = e.reason.split(' ')[0] || 'other';
|
|
907
|
+
cats[cat] = (cats[cat] || 0) + 1;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// Top categories as mini bar chart
|
|
911
|
+
const sorted = Object.entries(cats).sort((a, b) => b[1] - a[1]).slice(0, 5);
|
|
912
|
+
for (const [cat, count] of sorted) {
|
|
913
|
+
const bar = '█'.repeat(Math.min(count, 30));
|
|
914
|
+
const color = cat.match(/rm|reset|clean|Remove/i) ? c.red : cat.match(/push|force/i) ? c.red : c.yellow;
|
|
915
|
+
console.log(` ${color}${bar}${c.reset} ${count}× ${cat}`);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// Show last 3 entries of the day
|
|
919
|
+
const recent = dayEntries.slice(-3);
|
|
920
|
+
for (const e of recent) {
|
|
921
|
+
const time = (e.time.split('T')[1] || '').replace(/\+.*/, '').substring(0, 8);
|
|
922
|
+
console.log(c.dim + ` ${time} ${e.reason.substring(0, 40)} → ${e.cmd.substring(0, 50)}` + c.reset);
|
|
923
|
+
}
|
|
924
|
+
console.log();
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// Summary
|
|
928
|
+
console.log(c.bold + ' Summary' + c.reset);
|
|
929
|
+
console.log(` Total blocks: ${entries.length}`);
|
|
930
|
+
console.log(` Days with blocks: ${Object.keys(days).length}`);
|
|
931
|
+
console.log(` Avg per day: ${Math.round(entries.length / Math.max(Object.keys(days).length, 1))}`);
|
|
932
|
+
console.log();
|
|
933
|
+
}
|
|
934
|
+
|
|
856
935
|
async function guard(description) {
|
|
857
936
|
if (!description) {
|
|
858
937
|
console.log();
|
|
@@ -3905,6 +3984,7 @@ async function main() {
|
|
|
3905
3984
|
if (FULL) return fullSetup();
|
|
3906
3985
|
if (DOCTOR) return doctor();
|
|
3907
3986
|
if (WATCH) return watch();
|
|
3987
|
+
if (REPLAY) return replay();
|
|
3908
3988
|
if (GUARD_IDX !== -1) return guard(GUARD_DESC);
|
|
3909
3989
|
if (DIFF_HOOKS_IDX !== -1) return diffHooks(DIFF_HOOKS);
|
|
3910
3990
|
if (FROM_CLAUDEMD) return fromClaudeMd();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cc-safe-setup",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.6.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": {
|