cold-shower 2.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/.claude-plugin/marketplace.json +17 -0
- package/.claude-plugin/plugin.json +42 -0
- package/README.md +264 -0
- package/bin/cold-shower.js +120 -0
- package/hooks/activate.js +40 -0
- package/hooks/capture.js +132 -0
- package/hooks/gate.js +181 -0
- package/hooks/package.json +3 -0
- package/hooks/trigger.js +164 -0
- package/package.json +34 -0
- package/skills/cold-shower/SKILL.md +1020 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
|
|
3
|
+
"name": "cold-shower",
|
|
4
|
+
"description": "Reality check for vibe-coded apps. Vibe Score 0-100, LLM cost audit, AI security scan, git hygiene, and plan-gate enforcement.",
|
|
5
|
+
"owner": {
|
|
6
|
+
"name": "PradiptaPutra",
|
|
7
|
+
"url": "https://github.com/PradiptaPutra"
|
|
8
|
+
},
|
|
9
|
+
"plugins": [
|
|
10
|
+
{
|
|
11
|
+
"name": "cold-shower",
|
|
12
|
+
"description": "Six parallel audits → Vibe Score. Plan-gate blocks edits until approved. Persistent second brain across sessions.",
|
|
13
|
+
"source": "./",
|
|
14
|
+
"category": "productivity"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cold-shower",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Reality check for vibe-coded apps. 6 audits → Vibe Score 0-100. Plan-gate blocks edits until approved. Persistent second brain across sessions.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "PradiptaPutra",
|
|
7
|
+
"url": "https://github.com/PradiptaPutra"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/PradiptaPutra/cold-shower",
|
|
10
|
+
"repository": "https://github.com/PradiptaPutra/cold-shower",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"hooks": {
|
|
13
|
+
"SessionStart": [{
|
|
14
|
+
"hooks": [{
|
|
15
|
+
"type": "command",
|
|
16
|
+
"command": "node \"${CLAUDE_PLUGIN_DIR}/hooks/activate.js\"",
|
|
17
|
+
"timeout": 5
|
|
18
|
+
}]
|
|
19
|
+
}],
|
|
20
|
+
"UserPromptSubmit": [{
|
|
21
|
+
"hooks": [{
|
|
22
|
+
"type": "command",
|
|
23
|
+
"command": "node \"${CLAUDE_PLUGIN_DIR}/hooks/trigger.js\"",
|
|
24
|
+
"timeout": 10
|
|
25
|
+
}]
|
|
26
|
+
}],
|
|
27
|
+
"PreToolUse": [{
|
|
28
|
+
"hooks": [{
|
|
29
|
+
"type": "command",
|
|
30
|
+
"command": "node \"${CLAUDE_PLUGIN_DIR}/hooks/gate.js\"",
|
|
31
|
+
"timeout": 5
|
|
32
|
+
}]
|
|
33
|
+
}],
|
|
34
|
+
"Stop": [{
|
|
35
|
+
"hooks": [{
|
|
36
|
+
"type": "command",
|
|
37
|
+
"command": "node \"${CLAUDE_PLUGIN_DIR}/hooks/capture.js\"",
|
|
38
|
+
"timeout": 10
|
|
39
|
+
}]
|
|
40
|
+
}]
|
|
41
|
+
}
|
|
42
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/logo.png" width="180" alt="cold-shower"/>
|
|
4
|
+
|
|
5
|
+
# cold-shower
|
|
6
|
+
|
|
7
|
+
**The reality check your vibe-coded app didn't ask for.**
|
|
8
|
+
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](https://claude.ai/code)
|
|
11
|
+
[](#)
|
|
12
|
+
[](#-install)
|
|
13
|
+
|
|
14
|
+
*One install. Three modes. Auto-triggers. No commands to memorize.*
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 🥶 Why this exists
|
|
21
|
+
|
|
22
|
+
> **65%** of vibe-coded production apps have at least one security vulnerability.
|
|
23
|
+
> **45%** of AI-generated code fails basic security checks.
|
|
24
|
+
> **2x** more secrets get leaked in AI-assisted commits vs human-written code.
|
|
25
|
+
|
|
26
|
+
You shipped fast. Claude helped. But nobody audited what came out.
|
|
27
|
+
|
|
28
|
+
**cold-shower does.**
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## ⚡ Three modes, one skill
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
36
|
+
│ cold-shower │
|
|
37
|
+
├──────────────┬──────────────────────┬───────────────────────┤
|
|
38
|
+
│ 🔍 AUDIT │ 📋 PLAN-GATE │ 🧠 RECALL │
|
|
39
|
+
│ │ │ │
|
|
40
|
+
│ 6 parallel │ Structured plan │ Persistent second │
|
|
41
|
+
│ audits → │ BEFORE any code. │ brain across all │
|
|
42
|
+
│ Vibe Score │ Blocks edits via │ sessions. Replaces │
|
|
43
|
+
│ 0–100 │ PreToolUse hook │ Obsidian for devs. │
|
|
44
|
+
│ │ until APPROVED. │ │
|
|
45
|
+
│ "audit me" │ "implement X" │ "remember this" │
|
|
46
|
+
└─────────────┴──────────────────────┴───────────────────────-┘
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 🚿 Install
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
curl -fsSL https://raw.githubusercontent.com/PradiptaPutra/cold-shower/main/install.sh | bash
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Done. Works on every project you open from that moment forward.
|
|
58
|
+
|
|
59
|
+
**What the installer wires:**
|
|
60
|
+
|
|
61
|
+
| Hook | Event | Does |
|
|
62
|
+
|------|-------|------|
|
|
63
|
+
| `activate.js` | `SessionStart` | Injects skill + loads brain memories |
|
|
64
|
+
| `trigger.js` | `UserPromptSubmit` | Detects intent, routes to correct mode |
|
|
65
|
+
| `gate.js` | `PreToolUse` | Blocks edits until plan approved |
|
|
66
|
+
| `capture.js` | `Stop` | Suggests memories to save at session end |
|
|
67
|
+
|
|
68
|
+
Idempotent — safe to re-run for updates.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 🤖 You never type a command
|
|
73
|
+
|
|
74
|
+
| You say... | Mode | What happens |
|
|
75
|
+
|-----------|------|-------------|
|
|
76
|
+
| `"audit my codebase"` | 🔍 | Full 6-audit health check → Vibe Score |
|
|
77
|
+
| `"is this ready to deploy?"` | 🔍 | Pre-deploy gate scan |
|
|
78
|
+
| `"my LLM bill is insane"` | 🔍 | Cost audit → caching + routing fixes |
|
|
79
|
+
| `"app crashed on Product Hunt"` | 🔍 | Emergency mode → 5-min triage |
|
|
80
|
+
| `"re-audit"` | 🔍 | Re-runs, compares to last score |
|
|
81
|
+
| `"implement stripe payments"` | 📋 | Structured plan → blocks edits until APPROVED |
|
|
82
|
+
| `"fix this bug"` | 📋 | Plan first, then implementation |
|
|
83
|
+
| `"I committed my .env"` | 🔴 | Rotate secrets + scrub git history |
|
|
84
|
+
| `"remember this decision"` | 🧠 | Saves to brain with WHY + date |
|
|
85
|
+
| `"what did we decide about auth"` | 🧠 | Searches brain files |
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 📊 Audit output
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
╔══════════════════════════════════════════════════════════════╗
|
|
93
|
+
║ 🚿 COLD SHOWER — myapp — 2026-06-29 ║
|
|
94
|
+
╚══════════════════════════════════════════════════════════════╝
|
|
95
|
+
|
|
96
|
+
📊 Last score: 45/100 (D) on 2026-06-15 — let's see if it improved.
|
|
97
|
+
|
|
98
|
+
VIBE SCORE: 71/100 Grade: C ▲ +26 from last audit
|
|
99
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
100
|
+
|
|
101
|
+
[A] LLM COSTS — ✅ CLEAN
|
|
102
|
+
[B] AI SECURITY — 1 issue (no per-user token budget)
|
|
103
|
+
[C] CODE HEALTH — 3 god files | 2.9% dup | 0 floating promises
|
|
104
|
+
[D] DEPENDENCIES — 18 moderate CVEs (dev-only) | 0 critical
|
|
105
|
+
[E] PROD READINESS — ✅ CLEAN
|
|
106
|
+
[F] GIT/DEVOPS — 2 issues (no Dependabot, no branch protection)
|
|
107
|
+
|
|
108
|
+
🔴 CRITICAL — None
|
|
109
|
+
|
|
110
|
+
🟡 HIGH
|
|
111
|
+
[B] No per-user token budget — one user can drain your OpenAI key
|
|
112
|
+
[C] AppShell.tsx 1768 lines — auth+routing+sidebar+chat all in one
|
|
113
|
+
|
|
114
|
+
🟢 QUICK WINS (15 min)
|
|
115
|
+
Add .github/dependabot.yml
|
|
116
|
+
Set branch protection on main
|
|
117
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
118
|
+
Score saved. History: 2 entries. Type re-audit after next sprint.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 🔍 What each audit catches
|
|
124
|
+
|
|
125
|
+
| # | Audit | Key checks |
|
|
126
|
+
|---|-------|-----------|
|
|
127
|
+
| 🤑 **A — LLM Costs** | Missing semantic cache, hardcoded expensive model, no conversation history trim |
|
|
128
|
+
| 🔐 **B — AI Security** | Prompt injection in your endpoints, PII sent to OpenAI, no per-user budget, jailbreak surface |
|
|
129
|
+
| 🧹 **C — Code Health** | God files >500 lines, circular deps, duplicate logic >10%, floating promises |
|
|
130
|
+
| 📦 **D — Dependencies** | Unused packages (knip), CVEs (npm audit), archived libs, semantic duplicates |
|
|
131
|
+
| 🚀 **E — Prod Readiness** | N+1 queries, connection pool exhaustion, no rate limits, missing indexes |
|
|
132
|
+
| 🛡️ **F — Git/DevOps** | `.env` committed, unpinned Actions, no CI, no branch protection, workflow injection |
|
|
133
|
+
|
|
134
|
+
**Audit F alone is worth the install.** CVE-2025-30066 (March 2025): a floating `@v4` Action tag got rewritten — 23,000 repos exposed. Audit F catches this.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 📋 Plan-gate — no more AI slop
|
|
139
|
+
|
|
140
|
+
Every time you say "implement X" or "fix X", cold-shower generates a structured plan **before writing a single line**:
|
|
141
|
+
|
|
142
|
+
```markdown
|
|
143
|
+
## Plan: Add Stripe payments
|
|
144
|
+
|
|
145
|
+
### Files to Touch
|
|
146
|
+
| File | Change |
|
|
147
|
+
|-----------------------|---------------|
|
|
148
|
+
| src/checkout/Form.tsx | Add Stripe UI |
|
|
149
|
+
| src/api/payments.ts | New endpoint |
|
|
150
|
+
|
|
151
|
+
### Files NOT to Touch
|
|
152
|
+
| File | Reason |
|
|
153
|
+
|-------------------------|-------------------------------|
|
|
154
|
+
| src/auth/middleware.ts | Fragile — race condition #234 |
|
|
155
|
+
|
|
156
|
+
### Pre-Mortem
|
|
157
|
+
"Most likely failure: webhook arrives before order row exists."
|
|
158
|
+
|
|
159
|
+
### Rollback
|
|
160
|
+
Revert 3 files. No migration needed.
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
→ **"Type `APPROVED` to proceed."**
|
|
164
|
+
|
|
165
|
+
The `PreToolUse` hook physically blocks all `Edit`/`Write` calls until you type APPROVED.
|
|
166
|
+
**No other Claude Code skill enforces planning at the hook level.**
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 🧠 Recall — second brain without Obsidian
|
|
171
|
+
|
|
172
|
+
| | Obsidian | cold-shower recall |
|
|
173
|
+
|--|---------|-------------------|
|
|
174
|
+
| Setup | Separate app + vault + MCP config | Included in `curl \| bash` |
|
|
175
|
+
| Capture | Manual note-taking | `"remember this"` or auto-suggested |
|
|
176
|
+
| Token cost | **~7M tokens** (full vault scan) | **~100 tokens** (grep) |
|
|
177
|
+
| Anti-regression | ❌ | ✅ Warns before touching fragile files |
|
|
178
|
+
| Survives context loss | Manually | Automatically |
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
~/.claude/brain/preferences.md ← global preferences
|
|
182
|
+
~/.claude/projects/myapp/brain/
|
|
183
|
+
decisions.md ← "chose Supabase — Prisma 380KB kills edge cold start"
|
|
184
|
+
avoid.md ← "don't touch auth — race condition, fixed 3 times"
|
|
185
|
+
bugs.md ← "N+1 in getUserList, fixed 2026-05-01, regression-prone"
|
|
186
|
+
context.md ← domain knowledge, user base, compliance requirements
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Before editing any file in `avoid.md`:
|
|
190
|
+
```
|
|
191
|
+
RECALL WARNING: auth/middleware.ts marked fragile (2026-05-01).
|
|
192
|
+
Reason: race condition in token refresh. Proceed carefully.
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 🏃 Fix sprints
|
|
198
|
+
|
|
199
|
+
Each sprint generates **working code files** — not a todo list.
|
|
200
|
+
|
|
201
|
+
| Sprint | Time | What gets generated |
|
|
202
|
+
|--------|------|-------------------|
|
|
203
|
+
| **0.5** 🔴 | 15 min | Secret rotation + `git filter-repo` history scrub |
|
|
204
|
+
| **1** | 30 min | Dep cleanup + `knip.json` |
|
|
205
|
+
| **2** | 2 hr | Rate limiting + async error handling |
|
|
206
|
+
| **3** | 2 hr | Connection pool fix + N+1 detection |
|
|
207
|
+
| **4** | 4 hr | `lib/llm-cache.ts` + `lib/llm-router.ts` + `lib/llm-client.ts` |
|
|
208
|
+
| **5** | 1 day | God component extraction playbook |
|
|
209
|
+
| **6** | 1 hr | `ci.yml` + `dependabot.yml` + `src/env.ts` + branch protection |
|
|
210
|
+
|
|
211
|
+
Sprint 0.5 fires **before everything else** if `.env` is committed. First action: rotate all secrets.
|
|
212
|
+
|
|
213
|
+
After any sprint → type `re-audit` → score compared to previous run automatically.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## 🆚 vs other top skills
|
|
218
|
+
|
|
219
|
+
| | 🪨 caveman (~78k ⭐) | 🐴 ponytail (~66k ⭐) | 🧊 cold-shower |
|
|
220
|
+
|--|-----|-----|-----|
|
|
221
|
+
| **What it changes** | How Claude *talks* | What Claude *builds* | What you *already built* |
|
|
222
|
+
| **When it acts** | During session | During coding | Retrospective + pre-deploy |
|
|
223
|
+
| **Hook enforcement** | ❌ soft | ❌ soft | ✅ PreToolUse hard block |
|
|
224
|
+
| **Generates fix code** | ❌ | ❌ | ✅ real TS/JS files |
|
|
225
|
+
| **Persistent memory** | ❌ | ❌ | ✅ survives sessions |
|
|
226
|
+
| **Score over time** | ❌ | ❌ | ✅ history + trend |
|
|
227
|
+
| **Multi-platform** | ✅ 30+ | ✅ 16+ | Claude Code |
|
|
228
|
+
|
|
229
|
+
> caveman fixes verbosity. ponytail fixes over-engineering. cold-shower fixes what's already in production.
|
|
230
|
+
> Install all three — they don't overlap.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## 🎯 Stats that make this real
|
|
235
|
+
|
|
236
|
+
- **GitClear 2024:** AI code averages 12.3% duplication — triple human baseline. cold-shower Audit C catches this.
|
|
237
|
+
- **Escape.tech:** 65% of 1,400 vibe-coded apps have a security issue. Audit B catches the common patterns.
|
|
238
|
+
- **GitGuardian 2026:** AI-assisted commits have 2x the secret leak rate vs human code. Audit F catches committed secrets.
|
|
239
|
+
- **CVE-2025-30066:** Floating `@v4` tag rewritten → 23,000 repos exposed. Audit F flags all unpinned Actions.
|
|
240
|
+
- **Veracode 2026:** 45% of AI code from 100+ LLMs fails security checks. Audits B + D cover the main failure modes.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## 📋 Requirements
|
|
245
|
+
|
|
246
|
+
- [Claude Code](https://claude.ai/code) — skill uses native hook system
|
|
247
|
+
- Node.js 18+
|
|
248
|
+
- `gh` CLI — optional, enables branch protection check in Audit F
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## 🗺️ Roadmap
|
|
253
|
+
|
|
254
|
+
- [ ] Benchmarks: 15-repo study (Juice Shop, NodeGoat, real vibe-coded apps)
|
|
255
|
+
- [ ] Cursor `.cursorrules` port for wider reach
|
|
256
|
+
- [ ] `cold-shower diff` — scan only PR-changed files
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
<div align="center">
|
|
261
|
+
|
|
262
|
+
MIT · [Issues](https://github.com/PradiptaPutra/cold-shower/issues) · [PradiptaPutra](https://github.com/PradiptaPutra)
|
|
263
|
+
|
|
264
|
+
</div>
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const os = require('os')
|
|
7
|
+
|
|
8
|
+
const cmd = process.argv[2] || 'help'
|
|
9
|
+
const HOME = os.homedir()
|
|
10
|
+
const SKILLS_DIR = path.join(HOME, '.claude', 'skills', 'cold-shower')
|
|
11
|
+
const HOOKS_DIR = path.join(HOME, '.claude', 'hooks')
|
|
12
|
+
const BRAIN_DIR = path.join(HOME, '.claude', 'brain')
|
|
13
|
+
const SETTINGS = path.join(HOME, '.claude', 'settings.json')
|
|
14
|
+
const PKG_ROOT = path.join(__dirname, '..')
|
|
15
|
+
|
|
16
|
+
function copyFile(src, dest) {
|
|
17
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true })
|
|
18
|
+
fs.copyFileSync(src, dest)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function wireHook(settings, event, hookFile, label) {
|
|
22
|
+
settings.hooks = settings.hooks || {}
|
|
23
|
+
settings.hooks[event] = settings.hooks[event] || []
|
|
24
|
+
const command = `node "${path.join(HOOKS_DIR, hookFile)}"`
|
|
25
|
+
const timeout = hookFile.includes('capture') ? 10 : 5
|
|
26
|
+
const hook = { hooks: [{ type: 'command', command, timeout }] }
|
|
27
|
+
if (!JSON.stringify(settings.hooks[event]).includes(label)) {
|
|
28
|
+
settings.hooks[event].push(hook)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function install() {
|
|
33
|
+
console.log('\n🧊 Installing cold-shower v2...\n')
|
|
34
|
+
|
|
35
|
+
copyFile(
|
|
36
|
+
path.join(PKG_ROOT, 'skills', 'cold-shower', 'SKILL.md'),
|
|
37
|
+
path.join(SKILLS_DIR, 'SKILL.md')
|
|
38
|
+
)
|
|
39
|
+
console.log('✓ Skill installed')
|
|
40
|
+
|
|
41
|
+
const hooks = [
|
|
42
|
+
['activate.js', 'cold-shower-activate.js'],
|
|
43
|
+
['trigger.js', 'cold-shower-trigger.js'],
|
|
44
|
+
['gate.js', 'cold-shower-gate.js'],
|
|
45
|
+
['capture.js', 'cold-shower-capture.js'],
|
|
46
|
+
]
|
|
47
|
+
hooks.forEach(([src, dest]) => copyFile(path.join(PKG_ROOT, 'hooks', src), path.join(HOOKS_DIR, dest)))
|
|
48
|
+
console.log('✓ Hooks copied')
|
|
49
|
+
|
|
50
|
+
let settings = {}
|
|
51
|
+
try { settings = JSON.parse(fs.readFileSync(SETTINGS, 'utf8')) } catch {}
|
|
52
|
+
wireHook(settings, 'SessionStart', 'cold-shower-activate.js', 'cold-shower-activate')
|
|
53
|
+
wireHook(settings, 'UserPromptSubmit', 'cold-shower-trigger.js', 'cold-shower-trigger')
|
|
54
|
+
wireHook(settings, 'PreToolUse', 'cold-shower-gate.js', 'cold-shower-gate')
|
|
55
|
+
wireHook(settings, 'Stop', 'cold-shower-capture.js', 'cold-shower-capture')
|
|
56
|
+
fs.writeFileSync(SETTINGS, JSON.stringify(settings, null, 2))
|
|
57
|
+
console.log('✓ Hooks wired into ~/.claude/settings.json')
|
|
58
|
+
|
|
59
|
+
fs.mkdirSync(BRAIN_DIR, { recursive: true })
|
|
60
|
+
console.log('✓ Brain directory created\n')
|
|
61
|
+
|
|
62
|
+
console.log(`cold-shower v2 installed. Open a new Claude Code session to activate.
|
|
63
|
+
|
|
64
|
+
Three modes — all auto-trigger:
|
|
65
|
+
🔍 Audit: "audit my codebase", "about to deploy", "re-audit"
|
|
66
|
+
📋 Plan: "implement X", "add X", "fix X", "refactor X"
|
|
67
|
+
🧠 Recall: "remember this", "what did we decide", "/recall"
|
|
68
|
+
`)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function update() {
|
|
72
|
+
console.log('\n🧊 Updating cold-shower...\n')
|
|
73
|
+
copyFile(
|
|
74
|
+
path.join(PKG_ROOT, 'skills', 'cold-shower', 'SKILL.md'),
|
|
75
|
+
path.join(SKILLS_DIR, 'SKILL.md')
|
|
76
|
+
)
|
|
77
|
+
const hooks = [
|
|
78
|
+
['activate.js', 'cold-shower-activate.js'],
|
|
79
|
+
['trigger.js', 'cold-shower-trigger.js'],
|
|
80
|
+
['gate.js', 'cold-shower-gate.js'],
|
|
81
|
+
['capture.js', 'cold-shower-capture.js'],
|
|
82
|
+
]
|
|
83
|
+
hooks.forEach(([src, dest]) => {
|
|
84
|
+
try { copyFile(path.join(PKG_ROOT, 'hooks', src), path.join(HOOKS_DIR, dest)) } catch {}
|
|
85
|
+
})
|
|
86
|
+
console.log('✓ Updated. Restart Claude Code session to apply.')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function uninstall() {
|
|
90
|
+
console.log('\n🧊 Uninstalling cold-shower...\n')
|
|
91
|
+
try { fs.rmSync(SKILLS_DIR, { recursive: true }) } catch {}
|
|
92
|
+
const hookDest = ['cold-shower-activate.js','cold-shower-trigger.js','cold-shower-gate.js','cold-shower-capture.js']
|
|
93
|
+
hookDest.forEach(f => { try { fs.unlinkSync(path.join(HOOKS_DIR, f)) } catch {} })
|
|
94
|
+
try {
|
|
95
|
+
let s = JSON.parse(fs.readFileSync(SETTINGS, 'utf8'))
|
|
96
|
+
;['SessionStart','UserPromptSubmit','PreToolUse','Stop'].forEach(event => {
|
|
97
|
+
if (s.hooks?.[event]) {
|
|
98
|
+
s.hooks[event] = s.hooks[event].filter(h => !JSON.stringify(h).includes('cold-shower'))
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
fs.writeFileSync(SETTINGS, JSON.stringify(s, null, 2))
|
|
102
|
+
} catch {}
|
|
103
|
+
console.log('✓ cold-shower removed.')
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function help() {
|
|
107
|
+
console.log(`
|
|
108
|
+
🧊 cold-shower v2 — Reality check for vibe-coded apps
|
|
109
|
+
|
|
110
|
+
Usage:
|
|
111
|
+
npx cold-shower install Install skill + wire all 4 hooks
|
|
112
|
+
npx cold-shower update Update to latest version
|
|
113
|
+
npx cold-shower uninstall Remove everything
|
|
114
|
+
|
|
115
|
+
github.com/PradiptaPutra/cold-shower
|
|
116
|
+
`)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const commands = { install, update, uninstall, help }
|
|
120
|
+
;(commands[cmd] || help)()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// SessionStart hook — injects cold-shower skill into every Claude Code session.
|
|
3
|
+
// User never needs to type /cold-shower — Claude already knows it and offers it.
|
|
4
|
+
|
|
5
|
+
const fs = require('fs')
|
|
6
|
+
const path = require('path')
|
|
7
|
+
|
|
8
|
+
const locations = [
|
|
9
|
+
process.env.CLAUDE_PLUGIN_ROOT
|
|
10
|
+
? path.join(process.env.CLAUDE_PLUGIN_ROOT, 'skills', 'cold-shower', 'SKILL.md')
|
|
11
|
+
: null,
|
|
12
|
+
path.join(__dirname, '..', 'skills', 'cold-shower', 'SKILL.md'),
|
|
13
|
+
path.join(process.env.HOME || '', '.claude', 'skills', 'cold-shower', 'SKILL.md'),
|
|
14
|
+
].filter(Boolean)
|
|
15
|
+
|
|
16
|
+
const skillPath = locations.find(p => { try { return fs.existsSync(p) } catch { return false } })
|
|
17
|
+
if (!skillPath) process.exit(0)
|
|
18
|
+
|
|
19
|
+
process.stdout.write(fs.readFileSync(skillPath, 'utf8'))
|
|
20
|
+
|
|
21
|
+
// Inject recall memories from brain files
|
|
22
|
+
const os = require('os')
|
|
23
|
+
const projectName = path.basename(process.cwd())
|
|
24
|
+
const brainFiles = [
|
|
25
|
+
path.join(os.homedir(), '.claude', 'brain', 'preferences.md'),
|
|
26
|
+
path.join(os.homedir(), '.claude', 'projects', projectName, 'brain', 'context.md'),
|
|
27
|
+
path.join(os.homedir(), '.claude', 'projects', projectName, 'brain', 'avoid.md'),
|
|
28
|
+
path.join(os.homedir(), '.claude', 'projects', projectName, 'brain', 'decisions.md'),
|
|
29
|
+
]
|
|
30
|
+
const memoryLines = []
|
|
31
|
+
for (const f of brainFiles) {
|
|
32
|
+
try {
|
|
33
|
+
const content = fs.readFileSync(f, 'utf8')
|
|
34
|
+
const headers = content.split('\n').filter(l => l.startsWith('##')).slice(0, 5)
|
|
35
|
+
if (headers.length > 0) memoryLines.push(...headers)
|
|
36
|
+
} catch {}
|
|
37
|
+
}
|
|
38
|
+
if (memoryLines.length > 0) {
|
|
39
|
+
process.stdout.write('\n\n## RECALL: Project Memory\n' + memoryLines.slice(0, 15).join('\n') + '\n')
|
|
40
|
+
}
|
package/hooks/capture.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Stop hook — scans session transcript at session end and suggests memories to save.
|
|
3
|
+
// Fires once when Claude Code finishes a session. Silent on error — never breaks shutdown.
|
|
4
|
+
|
|
5
|
+
const path = require('path')
|
|
6
|
+
|
|
7
|
+
const DECISIONS_PATTERNS = [
|
|
8
|
+
"we'll use", "we will use", "we decided", "going with", "chose ", "chosen ",
|
|
9
|
+
"the reason we", "instead of", "switching to", "moved to", "migrated to",
|
|
10
|
+
]
|
|
11
|
+
const AVOID_PATTERNS = [
|
|
12
|
+
"don't touch", "do not touch", "leave alone", "leave it alone",
|
|
13
|
+
"fragile", "be careful with", "be careful about", "this is tricky",
|
|
14
|
+
"handle with care", "avoid touching", "don't modify",
|
|
15
|
+
]
|
|
16
|
+
const BUG_PATTERNS = [
|
|
17
|
+
"the bug was", "fixed by", "the issue was", "root cause", "to fix this",
|
|
18
|
+
"the fix was", "was caused by", "turned out to be", "discovered that",
|
|
19
|
+
]
|
|
20
|
+
const CONTEXT_PATTERNS = [
|
|
21
|
+
"this project", "the users are", "this app is", "remember that",
|
|
22
|
+
"important to note", "worth noting", "key thing is", "the system",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
function classify(lower) {
|
|
26
|
+
if (AVOID_PATTERNS.some(p => lower.includes(p))) return 'avoid'
|
|
27
|
+
if (BUG_PATTERNS.some(p => lower.includes(p))) return 'bugs'
|
|
28
|
+
if (DECISIONS_PATTERNS.some(p => lower.includes(p))) return 'decisions'
|
|
29
|
+
if (CONTEXT_PATTERNS.some(p => lower.includes(p))) return 'context'
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Extract a short one-line summary from the surrounding sentence.
|
|
34
|
+
function extractSnippet(content, matchIndex) {
|
|
35
|
+
// Walk back to sentence start
|
|
36
|
+
let start = matchIndex
|
|
37
|
+
while (start > 0 && content[start - 1] !== '.' && content[start - 1] !== '\n') start--
|
|
38
|
+
// Walk forward to sentence end or 120 chars
|
|
39
|
+
let end = matchIndex
|
|
40
|
+
while (end < content.length && content[end] !== '.' && content[end] !== '\n' && end - start < 120) end++
|
|
41
|
+
return content.slice(start, end).trim().replace(/\s+/g, ' ')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function scanMessages(transcript) {
|
|
45
|
+
const candidates = []
|
|
46
|
+
|
|
47
|
+
for (const msg of transcript) {
|
|
48
|
+
if (msg.role !== 'assistant') continue
|
|
49
|
+
const raw = typeof msg.content === 'string'
|
|
50
|
+
? msg.content
|
|
51
|
+
: Array.isArray(msg.content)
|
|
52
|
+
? msg.content.map(b => (typeof b === 'string' ? b : b.text || '')).join(' ')
|
|
53
|
+
: ''
|
|
54
|
+
if (!raw) continue
|
|
55
|
+
const lower = raw.toLowerCase()
|
|
56
|
+
|
|
57
|
+
// Find the first matching pattern in this message
|
|
58
|
+
const allPatterns = [
|
|
59
|
+
...AVOID_PATTERNS,
|
|
60
|
+
...BUG_PATTERNS,
|
|
61
|
+
...DECISIONS_PATTERNS,
|
|
62
|
+
...CONTEXT_PATTERNS,
|
|
63
|
+
]
|
|
64
|
+
for (const pat of allPatterns) {
|
|
65
|
+
const idx = lower.indexOf(pat)
|
|
66
|
+
if (idx === -1) continue
|
|
67
|
+
const category = classify(lower)
|
|
68
|
+
if (!category) continue
|
|
69
|
+
const snippet = extractSnippet(raw, idx)
|
|
70
|
+
if (snippet.length < 10) continue
|
|
71
|
+
// Avoid duplicate snippets
|
|
72
|
+
if (!candidates.some(c => c.snippet === snippet)) {
|
|
73
|
+
candidates.push({ category, snippet })
|
|
74
|
+
}
|
|
75
|
+
break // one candidate per assistant message
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return candidates
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function buildTag(category) {
|
|
83
|
+
const map = { decisions: '[decisions]', avoid: '[avoid]', bugs: '[bugs]', context: '[context]' }
|
|
84
|
+
return map[category] || '[context]'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function buildOutput(candidates, projectName) {
|
|
88
|
+
// Pick up to 5, most recent (last in array = most recent)
|
|
89
|
+
const top = candidates.slice(-5)
|
|
90
|
+
if (top.length === 0) return '{}'
|
|
91
|
+
|
|
92
|
+
const brainBase = `~/.claude/projects/${projectName}/brain`
|
|
93
|
+
const fileMap = {
|
|
94
|
+
decisions: `${brainBase}/decisions.md`,
|
|
95
|
+
avoid: `${brainBase}/avoid.md`,
|
|
96
|
+
bugs: `${brainBase}/bugs.md`,
|
|
97
|
+
context: `${brainBase}/context.md`,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const lines = top.map((c, i) => `${i + 1}. ${buildTag(c.category)} ${c.snippet}`)
|
|
101
|
+
|
|
102
|
+
const fileHints = [...new Set(top.map(c => fileMap[c.category]))]
|
|
103
|
+
.map(f => ` → ${f}`)
|
|
104
|
+
.join('\n')
|
|
105
|
+
|
|
106
|
+
const block = [
|
|
107
|
+
'COLD-SHOWER RECALL: Session ended. Suggested memories to save:',
|
|
108
|
+
'',
|
|
109
|
+
...lines,
|
|
110
|
+
'',
|
|
111
|
+
`Memory files:\n${fileHints}`,
|
|
112
|
+
'',
|
|
113
|
+
"To save: tell Claude 'remember #1' or 'save all'. To skip: 'no memories'.",
|
|
114
|
+
'Or type /recall to manage memories anytime.',
|
|
115
|
+
].join('\n')
|
|
116
|
+
|
|
117
|
+
return JSON.stringify({ additionalContext: block })
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let input = ''
|
|
121
|
+
process.stdin.on('data', chunk => { input += chunk })
|
|
122
|
+
process.stdin.on('end', () => {
|
|
123
|
+
try {
|
|
124
|
+
const data = JSON.parse(input)
|
|
125
|
+
const transcript = Array.isArray(data.transcript) ? data.transcript : []
|
|
126
|
+
const candidates = scanMessages(transcript)
|
|
127
|
+
const projectName = path.basename(process.cwd())
|
|
128
|
+
process.stdout.write(buildOutput(candidates, projectName))
|
|
129
|
+
} catch {
|
|
130
|
+
process.stdout.write('{}')
|
|
131
|
+
}
|
|
132
|
+
})
|