cc-safe-setup 3.8.0 → 4.0.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 CHANGED
@@ -224,6 +224,8 @@ Or browse all available examples in [`examples/`](examples/):
224
224
  - **session-handoff.sh** — Auto-save git state and session info to `~/.claude/session-handoff.md` on session end
225
225
  - **diff-size-guard.sh** — Warn/block when committing too many files at once (default: warn at 10, block at 50)
226
226
  - **dependency-audit.sh** — Warn when installing packages not in manifest (npm/pip/cargo supply chain awareness)
227
+ - **cost-tracker.sh** — Estimate session token cost and warn at thresholds ($1, $5)
228
+ - **read-before-edit.sh** — Warn when editing files not recently read (prevents old_string mismatches)
227
229
 
228
230
  ## Safety Checklist
229
231
 
@@ -0,0 +1,83 @@
1
+ # cc-safe-setup Roadmap
2
+
3
+ ## Current: v3.8.1
4
+
5
+ - 8 built-in hooks + 38 examples
6
+ - 21 CLI commands
7
+ - 5 web tools (audit, cheatsheet, ecosystem, cookbook, hook builder)
8
+ - 173 tests, CI green
9
+ - 2,500+ daily npm downloads
10
+
11
+ ## Next Major: v4.0 (planned)
12
+
13
+ ### --dashboard: Real-Time Terminal Dashboard
14
+
15
+ A single screen showing everything about your Claude Code safety:
16
+
17
+ ```
18
+ ┌─ cc-safe-setup dashboard ─────────────────────────┐
19
+ │ │
20
+ │ Hooks: 26 active (8 built-in + 18 examples) │
21
+ │ Score: 85/100 (Grade A) │
22
+ │ Context: ~60% remaining │
23
+ │ Cost: ~$1.47 (142 tool calls, Opus) │
24
+ │ │
25
+ │ ── Recent Blocks ──────────────────────────────── │
26
+ │ 14:23 rm -rf ~/projects (destructive-guard) │
27
+ │ 14:21 git push --force (branch-guard) │
28
+ │ 14:18 git add .env (secret-guard) │
29
+ │ │
30
+ │ ── Hook Performance ───────────────────────────── │
31
+ │ destructive-guard 15ms ████████ │
32
+ │ branch-guard 7ms ████ │
33
+ │ secret-guard 5ms ███ │
34
+ │ │
35
+ │ ── Today ──────────────────────────────────────── │
36
+ │ Blocks: 12 | Warns: 5 | Approves: 34 │
37
+ │ Top reason: rm on sensitive path (5) │
38
+ └─────────────────────────────────────────────────────┘
39
+ ```
40
+
41
+ **Implementation notes:**
42
+ - ANSI escape codes only (no blessed/ink dependencies)
43
+ - Reads blocked-commands.log + context-monitor state + cost-tracker state
44
+ - Refreshes every 2 seconds
45
+ - Ctrl+C to exit
46
+ - Works in any terminal (iTerm, VS Code, Windows Terminal, WSL)
47
+
48
+ ### Hook Marketplace (concept)
49
+
50
+ Community-contributed hooks discoverable from CLI:
51
+
52
+ ```bash
53
+ npx cc-safe-setup --search "database"
54
+ npx cc-safe-setup --install-remote user/hook-name
55
+ ```
56
+
57
+ Would require a registry (GitHub-based, no server). Defer to v5.0.
58
+
59
+ ### Hook Composition
60
+
61
+ Chain hooks with conditions:
62
+
63
+ ```json
64
+ {
65
+ "hooks": [{
66
+ "if": "branch === 'main'",
67
+ "then": "block-all-writes.sh",
68
+ "else": "allow-all.sh"
69
+ }]
70
+ }
71
+ ```
72
+
73
+ Requires Claude Code API changes. Not feasible with current hook system.
74
+
75
+ ## Done (Session 39)
76
+
77
+ - --create, --lint, --diff, --share, --benchmark, --doctor, --watch, --stats
78
+ - --export/--import, --audit --json
79
+ - 38 examples including cost-tracker, loop-detector, session-handoff
80
+ - Hook Builder web tool
81
+ - Interactive COOKBOOK
82
+ - 6 documentation files + Japanese README
83
+ - CONTRIBUTING.md for external contributors
@@ -0,0 +1,56 @@
1
+ #!/bin/bash
2
+ # ================================================================
3
+ # cost-tracker.sh — Estimate session token cost
4
+ # ================================================================
5
+ # PURPOSE:
6
+ # Claude Code doesn't show token costs. This hook tracks
7
+ # tool calls and estimates cumulative cost, warning at thresholds.
8
+ #
9
+ # TRIGGER: PostToolUse
10
+ # MATCHER: ""
11
+ #
12
+ # HOW IT WORKS:
13
+ # Counts tool calls as a proxy for token usage.
14
+ # Average tool call ≈ 2K tokens input + 1K output.
15
+ # Opus: $15/M input, $75/M output
16
+ # Sonnet: $3/M input, $15/M output
17
+ #
18
+ # CONFIGURATION:
19
+ # CC_COST_MODEL=opus (default) or sonnet
20
+ # CC_COST_WARN=1.00 warn at $1 (default)
21
+ # CC_COST_BLOCK=5.00 warn at $5 (default, doesn't block)
22
+ # ================================================================
23
+
24
+ COUNTER_FILE="/tmp/cc-cost-tracker-calls"
25
+ LAST_WARN="/tmp/cc-cost-tracker-warned"
26
+
27
+ COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo 0)
28
+ COUNT=$((COUNT + 1))
29
+ echo "$COUNT" > "$COUNTER_FILE"
30
+
31
+ MODEL="${CC_COST_MODEL:-opus}"
32
+ WARN="${CC_COST_WARN:-1.00}"
33
+ BLOCK="${CC_COST_BLOCK:-5.00}"
34
+
35
+ # Estimate: ~2K input + ~1K output tokens per tool call
36
+ if [ "$MODEL" = "opus" ]; then
37
+ # Opus: $15/M in, $75/M out → ~$0.105 per tool call
38
+ COST=$(echo "scale=2; $COUNT * 0.105" | bc 2>/dev/null || echo "0")
39
+ else
40
+ # Sonnet: $3/M in, $15/M out → ~$0.021 per tool call
41
+ COST=$(echo "scale=2; $COUNT * 0.021" | bc 2>/dev/null || echo "0")
42
+ fi
43
+
44
+ # Graduated warnings (with cooldown)
45
+ WARNED=$(cat "$LAST_WARN" 2>/dev/null || echo "0")
46
+
47
+ if [ "$(echo "$COST >= $BLOCK" | bc 2>/dev/null)" = "1" ] && [ "$WARNED" != "block" ]; then
48
+ echo "COST: ~\$${COST} estimated ($COUNT tool calls, $MODEL)" >&2
49
+ echo "Consider finishing current task and compacting." >&2
50
+ echo "block" > "$LAST_WARN"
51
+ elif [ "$(echo "$COST >= $WARN" | bc 2>/dev/null)" = "1" ] && [ "$WARNED" = "0" ]; then
52
+ echo "COST: ~\$${COST} estimated ($COUNT tool calls, $MODEL)" >&2
53
+ echo "warn" > "$LAST_WARN"
54
+ fi
55
+
56
+ exit 0
package/index.mjs CHANGED
@@ -84,6 +84,7 @@ const DIFF_IDX = process.argv.findIndex(a => a === '--diff');
84
84
  const DIFF_FILE = DIFF_IDX !== -1 ? process.argv[DIFF_IDX + 1] : null;
85
85
  const SHARE = process.argv.includes('--share');
86
86
  const BENCHMARK = process.argv.includes('--benchmark');
87
+ const DASHBOARD = process.argv.includes('--dashboard');
87
88
  const CREATE_IDX = process.argv.findIndex(a => a === '--create');
88
89
  const CREATE_DESC = CREATE_IDX !== -1 ? process.argv.slice(CREATE_IDX + 1).join(' ') : null;
89
90
 
@@ -105,6 +106,7 @@ if (HELP) {
105
106
  npx cc-safe-setup --audit --json Machine-readable output for CI/CD
106
107
  npx cc-safe-setup --scan Detect tech stack, recommend hooks
107
108
  npx cc-safe-setup --learn Learn from your block history
109
+ npx cc-safe-setup --dashboard Real-time status dashboard
108
110
  npx cc-safe-setup --benchmark Measure hook execution time
109
111
  npx cc-safe-setup --share Generate shareable URL for your setup
110
112
  npx cc-safe-setup --diff <file> Compare your settings with another file
@@ -356,6 +358,8 @@ function examples() {
356
358
  'commit-quality-gate.sh': 'Warn on vague or too-long commit messages',
357
359
  'diff-size-guard.sh': 'Warn/block on large diffs (10+ files warn, 50+ block)',
358
360
  'dependency-audit.sh': 'Warn on new package installs not in manifest',
361
+ 'cost-tracker.sh': 'Estimate session token cost ($1 warn, $5 alert)',
362
+ 'read-before-edit.sh': 'Warn when editing files not recently read',
359
363
  },
360
364
  };
361
365
 
@@ -786,6 +790,74 @@ async function fullSetup() {
786
790
  console.log();
787
791
  }
788
792
 
793
+ async function dashboard() {
794
+ const { createReadStream, watchFile } = await import('fs');
795
+ const { createInterface: createRL } = await import('readline');
796
+
797
+ const BLOCK_LOG = join(HOME, '.claude', 'blocked-commands.log');
798
+ const COST_FILE = '/tmp/cc-cost-tracker-calls';
799
+ const CONTEXT_FILE = '/tmp/cc-context-pct';
800
+
801
+ const clear = () => process.stdout.write('\x1b[2J\x1b[H');
802
+
803
+ // Count hooks
804
+ let hookCount = 0;
805
+ let exampleCount = 0;
806
+ if (existsSync(SETTINGS_PATH)) {
807
+ try {
808
+ const s = JSON.parse(readFileSync(SETTINGS_PATH, 'utf-8'));
809
+ for (const entries of Object.values(s.hooks || {})) {
810
+ hookCount += entries.reduce((n, e) => n + (e.hooks || []).length, 0);
811
+ }
812
+ } catch {}
813
+ }
814
+ exampleCount = existsSync(join(HOOKS_DIR)) ?
815
+ (await import('fs')).readdirSync(HOOKS_DIR).filter(f => f.endsWith('.sh')).length : 0;
816
+
817
+ function render() {
818
+ clear();
819
+
820
+ // Header
821
+ console.log(c.bold + ' cc-safe-setup --dashboard' + c.reset + ' ' + c.dim + new Date().toLocaleTimeString() + c.reset);
822
+ console.log(' ' + '─'.repeat(50));
823
+
824
+ // Status row
825
+ const context = existsSync(CONTEXT_FILE) ? readFileSync(CONTEXT_FILE, 'utf-8').trim() + '%' : '?';
826
+ const calls = existsSync(COST_FILE) ? readFileSync(COST_FILE, 'utf-8').trim() : '0';
827
+ const cost = (parseInt(calls) * 0.105).toFixed(2);
828
+
829
+ console.log(' Hooks: ' + c.green + hookCount + c.reset + ' registered | Scripts: ' + exampleCount);
830
+ console.log(' Context: ' + c.yellow + context + c.reset + ' | Cost: ~$' + cost + ' (' + calls + ' calls)');
831
+ console.log(' ' + '─'.repeat(50));
832
+
833
+ // Recent blocks
834
+ console.log(c.bold + ' Recent Blocks' + c.reset);
835
+ if (existsSync(BLOCK_LOG)) {
836
+ const lines = readFileSync(BLOCK_LOG, 'utf-8').split('\n').filter(l => l.trim());
837
+ const recent = lines.slice(-5);
838
+ for (const line of recent) {
839
+ const m = line.match(/^\[([^\]]+)\]\s*BLOCKED:\s*(.+?)\s*\|/);
840
+ if (m) {
841
+ const time = m[1].replace(/T/, ' ').replace(/\+.*/, '').slice(11, 16);
842
+ console.log(' ' + c.dim + time + c.reset + ' ' + c.red + m[2].trim() + c.reset);
843
+ }
844
+ }
845
+ if (recent.length === 0) console.log(c.dim + ' (none)' + c.reset);
846
+ } else {
847
+ console.log(c.dim + ' (no log yet)' + c.reset);
848
+ }
849
+
850
+ console.log(' ' + '─'.repeat(50));
851
+ console.log(c.dim + ' Refreshing every 3s. Ctrl+C to exit.' + c.reset);
852
+ }
853
+
854
+ render();
855
+ setInterval(render, 3000);
856
+
857
+ // Keep alive
858
+ await new Promise(() => {});
859
+ }
860
+
789
861
  async function benchmark() {
790
862
  const { spawnSync } = await import('child_process');
791
863
 
@@ -2073,6 +2145,7 @@ async function main() {
2073
2145
  if (FULL) return fullSetup();
2074
2146
  if (DOCTOR) return doctor();
2075
2147
  if (WATCH) return watch();
2148
+ if (DASHBOARD) return dashboard();
2076
2149
  if (BENCHMARK) return benchmark();
2077
2150
  if (SHARE) return share();
2078
2151
  if (DIFF_FILE) return diff(DIFF_FILE);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-safe-setup",
3
- "version": "3.8.0",
3
+ "version": "4.0.0",
4
4
  "description": "One command to make Claude Code safe for autonomous operation. 8 built-in + 36 examples. 21 commands: create, audit, lint, diff, share, benchmark, watch, learn. 2,500+ daily npm downloads.",
5
5
  "main": "index.mjs",
6
6
  "bin": {