@jhizzard/termdeck 1.0.1 → 1.0.2

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,172 @@
1
+ # TermDeck session-end memory hook
2
+
3
+ The `@jhizzard/termdeck-stack` installer can drop `memory-session-end.js`
4
+ into `~/.claude/hooks/` and wire it into `~/.claude/settings.json` under
5
+ `hooks.Stop`. The installer prompts you before doing this; default is
6
+ yes.
7
+
8
+ ## What the hook does
9
+
10
+ On every Claude Code session close, Claude Code fires its `Stop` hook
11
+ with a JSON payload on stdin:
12
+
13
+ ```json
14
+ { "transcript_path": "/path/to/session.jsonl", "cwd": "/path/where/you/were/working", "session_id": "..." }
15
+ ```
16
+
17
+ The hook:
18
+
19
+ 1. Skips transcripts smaller than 5 KB (no signal in tiny sessions —
20
+ override via `TERMDECK_HOOK_MIN_BYTES`).
21
+ 2. Validates env vars (`SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`,
22
+ `OPENAI_API_KEY`); if any are missing, logs the missing list and
23
+ exits cleanly without blocking the session close.
24
+ 3. Detects the project from `cwd` against a built-in regex table; falls
25
+ back to `"global"` when nothing matches. **The default table is
26
+ intentionally empty** — see "Customizing the project map" below to
27
+ add your own entries.
28
+ 4. Builds a coarse session summary from the last ~30 messages of the
29
+ transcript (~7 KB cap to stay inside OpenAI's embedding-input
30
+ budget).
31
+ 5. Embeds the summary via OpenAI `text-embedding-3-small` (1,536-dim).
32
+ 6. POSTs **one row** to Supabase `/rest/v1/memory_items` with
33
+ `source_type='session_summary'`.
34
+ 7. Logs every step to `~/.claude/hooks/memory-hook.log`.
35
+
36
+ The hook is **fail-soft**: any error (network, parse, env-var-missing,
37
+ malformed transcript) is logged and the hook exits 0. Claude Code's
38
+ session close is never blocked.
39
+
40
+ ## Required environment
41
+
42
+ The hook needs three env vars at run time:
43
+
44
+ | Var | What | How to set |
45
+ |---|---|---|
46
+ | `SUPABASE_URL` | Your Supabase project URL (e.g. `https://abc.supabase.co`) | `~/.termdeck/secrets.env` (Tier 2) |
47
+ | `SUPABASE_SERVICE_ROLE_KEY` | Service-role key with INSERT on `memory_items`. **Not the anon key.** | `~/.termdeck/secrets.env` |
48
+ | `OPENAI_API_KEY` | OpenAI key for embedding inference | `~/.termdeck/secrets.env` or your shell |
49
+
50
+ Claude Code propagates the parent shell's environment into hook
51
+ processes, so anything in your shell init or
52
+ `~/.termdeck/secrets.env` (sourced by `scripts/start.sh` /
53
+ `npx @jhizzard/termdeck`) is visible to the hook.
54
+
55
+ **From v0.17.0**, the TermDeck server also merges
56
+ `~/.termdeck/secrets.env` directly into every PTY-spawned shell — so any
57
+ Claude Code panel launched inside TermDeck inherits `SUPABASE_URL` /
58
+ `SUPABASE_SERVICE_ROLE_KEY` / `OPENAI_API_KEY` even if the user's
59
+ parent shell never sourced the file. Concrete values in
60
+ `process.env` still win (parent-shell env takes precedence over the
61
+ file fallback). Standalone Claude Code launches outside TermDeck
62
+ still rely on the parent shell having sourced the file — for those,
63
+ the wizard can offer a one-line `~/.zshrc` source addition.
64
+
65
+ If any of the three is missing the log line will name them:
66
+
67
+ ```
68
+ [2026-04-27T21:30:00.000Z] env-var-missing: OPENAI_API_KEY — set these in ~/.termdeck/secrets.env or your shell to enable Mnestra ingestion. Skipping.
69
+ ```
70
+
71
+ ## Customizing the project map
72
+
73
+ The hook ships with an **empty `PROJECT_MAP`** by default — every
74
+ session lands under `project: 'global'` until you add entries. To add
75
+ your own:
76
+
77
+ 1. Open `~/.claude/hooks/memory-session-end.js` after the installer
78
+ has dropped it.
79
+ 2. Find the `PROJECT_MAP` array near the top of the file.
80
+ 3. Add one entry per project; each entry is `{ pattern, project }`
81
+ where `pattern` is a regex matched against `cwd`.
82
+
83
+ ### Order matters: most-specific-first
84
+
85
+ `detectProject(cwd)` returns the **first** matching entry. If a deep
86
+ project lives under a broader parent dir, the deep pattern must come
87
+ first or the parent will swallow it. This bug bit the TermDeck team in
88
+ Sprint 41 — every cwd under a `ChopinNashville/` parent was getting
89
+ tagged `chopin-nashville` because the parent-dir pattern came before
90
+ each sub-project's specific pattern.
91
+
92
+ Example showing the right ordering:
93
+
94
+ ```js
95
+ const PROJECT_MAP = [
96
+ // Specific code projects under a common parent — these MUST appear
97
+ // before the parent-dir catch-all below.
98
+ { pattern: /\/MyOrg\/SideProjects\/widget-app/i, project: 'widget-app' },
99
+ { pattern: /\/MyOrg\/SideProjects\/scheduler/i, project: 'scheduler' },
100
+ { pattern: /\/MyOrg\/2026\/festival\/podium/i, project: 'podium' },
101
+ { pattern: /\/MyOrg\/2026\/festival/i, project: 'festival' },
102
+
103
+ // Other top-level projects.
104
+ { pattern: /\/PVB\//i, project: 'pvb' },
105
+
106
+ // Catch-all for the parent dir — only matches when no specific
107
+ // project above matched first.
108
+ { pattern: /\/MyOrg(\/|$)/i, project: 'myorg-ops' },
109
+ ];
110
+ ```
111
+
112
+ For a worked example of a real production taxonomy (with explicit
113
+ priority ordering, alias documentation, and a structural-invariant
114
+ test), see [`docs/PROJECT-TAXONOMY.md`](https://github.com/jhizzard/termdeck/blob/main/docs/PROJECT-TAXONOMY.md)
115
+ in the TermDeck repo.
116
+
117
+ ### Other rules
118
+
119
+ - The map is local-only — it's never sent to any service. Editing it
120
+ takes effect on the next Claude Code session close (no restart
121
+ needed).
122
+ - Anything that doesn't match falls through to `'global'`.
123
+ - Adopt the module-export contract (`module.exports = { detectProject, PROJECT_MAP }`)
124
+ if you want to write a unit test that exercises your taxonomy. The
125
+ bundled hook already does this; if you copy-paste a custom hook,
126
+ preserve the `if (require.main === module)` guard around the stdin
127
+ reader so `require()` doesn't hang.
128
+
129
+ ## Coexistence with Joshua's `rag-system` hook
130
+
131
+ If you have Joshua's private `rag-system` repo and his rag-system-based
132
+ session hook installed, this bundled hook and that one can coexist:
133
+
134
+ - The bundled hook writes `source_type='session_summary'` — one row
135
+ per session, summary-only.
136
+ - The `rag-system` hook writes `source_type='fact'` — multiple rows
137
+ per session via Claude Haiku fact extraction + dedup.
138
+
139
+ Different `source_type` values mean the two paths don't dedup against
140
+ each other. If both are installed at the same path
141
+ (`~/.claude/hooks/memory-session-end.js`) the installer will prompt
142
+ before overwriting; choose accordingly.
143
+
144
+ ## How to disable
145
+
146
+ Two options:
147
+
148
+ 1. Edit `~/.claude/settings.json` and remove the entry under
149
+ `hooks.Stop` that references `memory-session-end.js`. Leave the
150
+ file in place; it simply won't fire.
151
+ 2. Or delete `~/.claude/hooks/memory-session-end.js` AND remove the
152
+ `settings.json` entry. (Removing only the file leaves a broken
153
+ `command` in settings — Claude Code will log a missing-file error
154
+ on every session close.)
155
+
156
+ Re-running `npx @jhizzard/termdeck-stack` after disabling will
157
+ re-prompt to install. Decline at the prompt to stay opted out.
158
+
159
+ ## Optional flags
160
+
161
+ | Env var | Effect |
162
+ |---|---|
163
+ | `TERMDECK_HOOK_DEBUG=1` | Verbose `[debug]` lines in the log |
164
+ | `TERMDECK_HOOK_MIN_BYTES=10000` | Override the 5 KB skip threshold |
165
+
166
+ ## Log file
167
+
168
+ `~/.claude/hooks/memory-hook.log` accumulates one line per session
169
+ event (skips, errors, ingests). The hook never rotates it. If it
170
+ grows unwieldy you can truncate it
171
+ (`: > ~/.claude/hooks/memory-hook.log`) without affecting hook
172
+ behavior.