cc-safe-setup 8.1.0 → 8.2.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 (2) hide show
  1. package/index.mjs +113 -0
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -91,6 +91,7 @@ const GENERATE_CI = process.argv.includes('--generate-ci');
91
91
  const REPORT = process.argv.includes('--report');
92
92
  const QUICKFIX = process.argv.includes('--quickfix');
93
93
  const SHIELD = process.argv.includes('--shield');
94
+ const ANALYZE = process.argv.includes('--analyze');
94
95
  const COMPARE_IDX = process.argv.findIndex(a => a === '--compare');
95
96
  const COMPARE = COMPARE_IDX !== -1 ? { a: process.argv[COMPARE_IDX + 1], b: process.argv[COMPARE_IDX + 2] } : null;
96
97
  const CREATE_IDX = process.argv.findIndex(a => a === '--create');
@@ -126,6 +127,7 @@ if (HELP) {
126
127
  npx cc-safe-setup --doctor Diagnose why hooks aren't working
127
128
  npx cc-safe-setup --watch Live dashboard of blocked commands
128
129
  npx cc-safe-setup --create "<desc>" Generate a custom hook from description
130
+ npx cc-safe-setup --analyze Analyze what Claude did in your last session
129
131
  npx cc-safe-setup --shield Maximum safety in one command (fix + scan + install + CLAUDE.md)
130
132
  npx cc-safe-setup --quickfix Auto-detect and fix common Claude Code problems
131
133
  npx cc-safe-setup --stats Block statistics and patterns report
@@ -833,6 +835,116 @@ async function fullSetup() {
833
835
  console.log();
834
836
  }
835
837
 
838
+ async function analyze() {
839
+ const { execSync } = await import('child_process');
840
+ const { readdirSync, statSync } = await import('fs');
841
+ console.log();
842
+ console.log(c.bold + ' cc-safe-setup --analyze' + c.reset);
843
+ console.log(c.dim + ' What Claude did in your sessions' + c.reset);
844
+ console.log();
845
+
846
+ // 1. Blocked commands log
847
+ const blockLog = join(HOME, '.claude', 'blocked-commands.log');
848
+ let blocks = [];
849
+ if (existsSync(blockLog)) {
850
+ const content = readFileSync(blockLog, 'utf-8');
851
+ blocks = content.split('\n').filter(l => l.trim());
852
+ const recent = blocks.slice(-20);
853
+ console.log(c.bold + ' Blocked Commands' + c.reset + c.dim + ` (${blocks.length} total)` + c.reset);
854
+ if (recent.length > 0) {
855
+ // Count by type
856
+ const types = {};
857
+ for (const line of blocks) {
858
+ const match = line.match(/BLOCKED:\s*(.+?)(?:\s*—|\s*\(|$)/);
859
+ if (match) {
860
+ const type = match[1].trim().substring(0, 40);
861
+ types[type] = (types[type] || 0) + 1;
862
+ }
863
+ }
864
+ const sorted = Object.entries(types).sort((a, b) => b[1] - a[1]);
865
+ for (const [type, count] of sorted.slice(0, 8)) {
866
+ const bar = '█'.repeat(Math.min(count, 20));
867
+ console.log(` ${c.red}${bar}${c.reset} ${count}× ${type}`);
868
+ }
869
+ } else {
870
+ console.log(c.green + ' No blocked commands recorded.' + c.reset);
871
+ }
872
+ console.log();
873
+ }
874
+
875
+ // 2. Git activity (last 24h)
876
+ console.log(c.bold + ' Git Activity (last 24h)' + c.reset);
877
+ try {
878
+ const log = execSync('git log --oneline --since="24 hours ago" 2>/dev/null', { encoding: 'utf-8' }).trim();
879
+ if (log) {
880
+ const commits = log.split('\n');
881
+ console.log(c.dim + ` ${commits.length} commits` + c.reset);
882
+ for (const commit of commits.slice(0, 10)) {
883
+ console.log(` ${c.blue}•${c.reset} ${commit}`);
884
+ }
885
+ if (commits.length > 10) console.log(c.dim + ` ... and ${commits.length - 10} more` + c.reset);
886
+ } else {
887
+ console.log(c.dim + ' No commits in last 24h' + c.reset);
888
+ }
889
+ } catch {
890
+ console.log(c.dim + ' Not in a git repository' + c.reset);
891
+ }
892
+ console.log();
893
+
894
+ // 3. Files changed (last 24h)
895
+ console.log(c.bold + ' Files Changed (last 24h)' + c.reset);
896
+ try {
897
+ const diff = execSync('git diff --stat HEAD~10 2>/dev/null || git diff --stat 2>/dev/null', { encoding: 'utf-8' }).trim();
898
+ if (diff) {
899
+ const lines = diff.split('\n');
900
+ const summary = lines[lines.length - 1];
901
+ console.log(c.dim + ` ${summary.trim()}` + c.reset);
902
+ }
903
+ } catch {}
904
+ console.log();
905
+
906
+ // 4. Hook health
907
+ console.log(c.bold + ' Hook Health' + c.reset);
908
+ const hookDir = join(HOME, '.claude', 'hooks');
909
+ if (existsSync(hookDir)) {
910
+ const hooks = readdirSync(hookDir).filter(f => f.endsWith('.sh') || f.endsWith('.py'));
911
+ let execCount = 0, nonExec = 0;
912
+ for (const h of hooks) {
913
+ const st = statSync(join(hookDir, h));
914
+ if (st.mode & 0o111) execCount++; else nonExec++;
915
+ }
916
+ console.log(` ${c.green}${execCount}${c.reset} hooks executable${nonExec > 0 ? `, ${c.red}${nonExec}${c.reset} missing permissions` : ''}`);
917
+ }
918
+
919
+ // 5. Context usage estimate
920
+ console.log();
921
+ console.log(c.bold + ' Session Estimates' + c.reset);
922
+ // Check tool call log if exists
923
+ const toolLog = join(HOME, '.claude', 'tool-calls.log');
924
+ if (existsSync(toolLog)) {
925
+ const logContent = readFileSync(toolLog, 'utf-8');
926
+ const calls = logContent.split('\n').filter(l => l.trim());
927
+ const today = new Date().toISOString().split('T')[0];
928
+ const todayCalls = calls.filter(l => l.includes(today));
929
+ console.log(` Tool calls today: ${todayCalls.length}`);
930
+ }
931
+
932
+ // Token budget state
933
+ const budgetFiles = existsSync('/tmp') ? readdirSync('/tmp').filter(f => f.startsWith('cc-token-budget-')) : [];
934
+ if (budgetFiles.length > 0) {
935
+ for (const bf of budgetFiles.slice(0, 3)) {
936
+ const tokens = parseInt(readFileSync(join('/tmp', bf), 'utf-8').trim()) || 0;
937
+ const costCents = Math.round(tokens * 75 / 10000);
938
+ console.log(` Estimated cost: ~$${(costCents / 100).toFixed(2)} (${tokens.toLocaleString()} tokens)`);
939
+ }
940
+ }
941
+
942
+ console.log();
943
+ console.log(c.dim + ' Tip: Use --stats for block history analytics' + c.reset);
944
+ console.log(c.dim + ' Tip: Use --dashboard for real-time monitoring' + c.reset);
945
+ console.log();
946
+ }
947
+
836
948
  async function shield() {
837
949
  const { execSync } = await import('child_process');
838
950
  const { readdirSync } = await import('fs');
@@ -3042,6 +3154,7 @@ async function main() {
3042
3154
  if (FULL) return fullSetup();
3043
3155
  if (DOCTOR) return doctor();
3044
3156
  if (WATCH) return watch();
3157
+ if (ANALYZE) return analyze();
3045
3158
  if (SHIELD) return shield();
3046
3159
  if (QUICKFIX) return quickfix();
3047
3160
  if (REPORT) return report();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-safe-setup",
3
- "version": "8.1.0",
3
+ "version": "8.2.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": {