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.
@@ -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: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
10
+ [![Claude Code](https://img.shields.io/badge/Claude%20Code-Skill-blueviolet)](https://claude.ai/code)
11
+ [![Version](https://img.shields.io/badge/version-2.0-brightgreen)](#)
12
+ [![Install](https://img.shields.io/badge/install-curl%20%7C%20bash-orange)](#-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 &nbsp;·&nbsp; [Issues](https://github.com/PradiptaPutra/cold-shower/issues) &nbsp;·&nbsp; [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
+ }
@@ -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
+ })