cc-safe-setup 10.5.0 → 10.7.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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/index.mjs +85 -3
  3. 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**. 37 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)
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
@@ -412,12 +414,14 @@ function examples() {
412
414
  };
413
415
 
414
416
  // Optional category filter: --examples safety, --examples ux, etc.
415
- const filterArg = process.argv[process.argv.indexOf('--examples') + 1] || process.argv[process.argv.indexOf('-e') + 1] || '';
416
- const filter = filterArg.toLowerCase();
417
+ const exIdx = Math.max(process.argv.indexOf('--examples'), process.argv.indexOf('-e'));
418
+ const nextArg = exIdx !== -1 ? (process.argv[exIdx + 1] || '') : '';
419
+ const filter = nextArg.startsWith('-') ? '' : nextArg.toLowerCase();
417
420
 
418
421
  console.log();
419
422
  console.log(c.bold + ' cc-safe-setup --examples' + c.reset + (filter ? ' ' + filter : ''));
420
- console.log(c.dim + ' 38 hooks beyond the 8 built-in ones' + c.reset);
423
+ const totalExamples = Object.values(CATEGORIES).reduce((sum, cat) => sum + Object.keys(cat).length, 0);
424
+ console.log(c.dim + ` ${totalExamples} hooks beyond the 8 built-in ones` + c.reset);
421
425
  if (filter) console.log(c.dim + ' Filter: ' + filter + c.reset);
422
426
  console.log();
423
427
 
@@ -853,6 +857,83 @@ async function fullSetup() {
853
857
  console.log();
854
858
  }
855
859
 
860
+ async function replay() {
861
+ console.log();
862
+ console.log(c.bold + ' cc-safe-setup --replay' + c.reset);
863
+ console.log(c.dim + ' Replay blocked commands timeline' + c.reset);
864
+ console.log();
865
+
866
+ const LOG_PATH = join(HOME, '.claude', 'blocked-commands.log');
867
+ if (!existsSync(LOG_PATH)) {
868
+ console.log(c.dim + ' No blocked commands log found.' + c.reset);
869
+ console.log(c.dim + ' Hooks will create it when they block something.' + c.reset);
870
+ return;
871
+ }
872
+
873
+ const content = readFileSync(LOG_PATH, 'utf-8');
874
+ const lines = content.split('\n').filter(l => l.trim());
875
+
876
+ if (lines.length === 0) {
877
+ console.log(c.dim + ' Log is empty — no commands blocked yet.' + c.reset);
878
+ return;
879
+ }
880
+
881
+ // Parse entries
882
+ const entries = [];
883
+ for (const line of lines) {
884
+ const match = line.match(/^\[([^\]]+)\]\s*BLOCKED:\s*(.+?)\s*\|\s*cmd:\s*(.+)$/);
885
+ if (match) {
886
+ entries.push({ time: match[1], reason: match[2].trim(), cmd: match[3].trim() });
887
+ }
888
+ }
889
+
890
+ // Group by day
891
+ const days = {};
892
+ for (const e of entries) {
893
+ const day = e.time.split('T')[0] || 'unknown';
894
+ if (!days[day]) days[day] = [];
895
+ days[day].push(e);
896
+ }
897
+
898
+ // Show last 7 days
899
+ const sortedDays = Object.keys(days).sort().slice(-7);
900
+
901
+ for (const day of sortedDays) {
902
+ const dayEntries = days[day];
903
+ console.log(c.bold + ` ${day}` + c.reset + c.dim + ` (${dayEntries.length} blocks)` + c.reset);
904
+
905
+ // Category counts
906
+ const cats = {};
907
+ for (const e of dayEntries) {
908
+ const cat = e.reason.split(' ')[0] || 'other';
909
+ cats[cat] = (cats[cat] || 0) + 1;
910
+ }
911
+
912
+ // Top categories as mini bar chart
913
+ const sorted = Object.entries(cats).sort((a, b) => b[1] - a[1]).slice(0, 5);
914
+ for (const [cat, count] of sorted) {
915
+ const bar = '█'.repeat(Math.min(count, 30));
916
+ const color = cat.match(/rm|reset|clean|Remove/i) ? c.red : cat.match(/push|force/i) ? c.red : c.yellow;
917
+ console.log(` ${color}${bar}${c.reset} ${count}× ${cat}`);
918
+ }
919
+
920
+ // Show last 3 entries of the day
921
+ const recent = dayEntries.slice(-3);
922
+ for (const e of recent) {
923
+ const time = (e.time.split('T')[1] || '').replace(/\+.*/, '').substring(0, 8);
924
+ console.log(c.dim + ` ${time} ${e.reason.substring(0, 40)} → ${e.cmd.substring(0, 50)}` + c.reset);
925
+ }
926
+ console.log();
927
+ }
928
+
929
+ // Summary
930
+ console.log(c.bold + ' Summary' + c.reset);
931
+ console.log(` Total blocks: ${entries.length}`);
932
+ console.log(` Days with blocks: ${Object.keys(days).length}`);
933
+ console.log(` Avg per day: ${Math.round(entries.length / Math.max(Object.keys(days).length, 1))}`);
934
+ console.log();
935
+ }
936
+
856
937
  async function guard(description) {
857
938
  if (!description) {
858
939
  console.log();
@@ -3905,6 +3986,7 @@ async function main() {
3905
3986
  if (FULL) return fullSetup();
3906
3987
  if (DOCTOR) return doctor();
3907
3988
  if (WATCH) return watch();
3989
+ if (REPLAY) return replay();
3908
3990
  if (GUARD_IDX !== -1) return guard(GUARD_DESC);
3909
3991
  if (DIFF_HOOKS_IDX !== -1) return diffHooks(DIFF_HOOKS);
3910
3992
  if (FROM_CLAUDEMD) return fromClaudeMd();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-safe-setup",
3
- "version": "10.5.0",
3
+ "version": "10.7.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": {