hmem-mcp 2.0.3 → 2.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.
- package/README.md +37 -0
- package/dist/cli-init.js +20 -4
- package/dist/cli-init.js.map +1 -1
- package/dist/hmem-config.d.ts +5 -32
- package/dist/hmem-config.js +5 -35
- package/dist/hmem-config.js.map +1 -1
- package/dist/hmem-store.d.ts +59 -9
- package/dist/hmem-store.js +340 -128
- package/dist/hmem-store.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +284 -76
- package/dist/mcp-server.js.map +1 -1
- package/dist/session-cache.d.ts +66 -0
- package/dist/session-cache.js +100 -0
- package/dist/session-cache.js.map +1 -0
- package/hmem_developer.hmem +0 -0
- package/package.json +3 -2
- package/skills/hmem-config/SKILL.md +15 -19
- package/skills/hmem-curate/SKILL.md +17 -0
- package/skills/hmem-read/SKILL.md +23 -10
- package/skills/hmem-self-curate/SKILL.md +113 -0
- package/skills/hmem-setup/SKILL.md +6 -16
- package/skills/hmem-write/SKILL.md +24 -5
- package/skills/hmem-save/SKILL.md +0 -128
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hmem-self-curate
|
|
3
|
+
description: Curate your own memory. Systematically review entries — mark obsolete, irrelevant, or favorite. Run periodically to keep memory clean.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Self-Curation: Review Your Own Memory
|
|
7
|
+
|
|
8
|
+
You are curating **your own** memory — not another agent's. You know best which entries are still relevant. Use your regular tools: `read_memory`, `update_memory`, `write_memory`.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Step 1 — Load overview
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
read_memory(titles_only=true)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This gives you every entry as a compact line: `ID date [flags] title`. Scan the full list before acting.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Step 2 — Identify candidates
|
|
23
|
+
|
|
24
|
+
Work through the list prefix by prefix. For each entry, decide:
|
|
25
|
+
|
|
26
|
+
| Decision | Action |
|
|
27
|
+
|----------|--------|
|
|
28
|
+
| Still valid and useful | Skip (no action needed) |
|
|
29
|
+
| Important reference I need every session | `update_memory(id="X", content="...", favorite=true)` |
|
|
30
|
+
| Outdated — a better entry exists | Mark obsolete (see Step 3) |
|
|
31
|
+
| Just noise — not wrong, but irrelevant | `update_memory(id="X", content="...", irrelevant=true)` |
|
|
32
|
+
| L1 wording is vague or misleading | `update_memory(id="X", content="Better wording")` |
|
|
33
|
+
| Sub-node has valuable reference info | `update_memory(id="X.N", content="...", favorite=true)` |
|
|
34
|
+
|
|
35
|
+
**Drill into entries** before judging: `read_memory(id="L0042")` to see L2 children. An entry with a weak L1 may have valuable detail underneath.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Step 3 — Marking obsolete
|
|
40
|
+
|
|
41
|
+
Obsolete requires a correction reference. Two patterns:
|
|
42
|
+
|
|
43
|
+
**Pattern A: Replacement exists already**
|
|
44
|
+
Find the existing entry that supersedes it, then mark:
|
|
45
|
+
```
|
|
46
|
+
update_memory(id="E0023", content="Wrong approach — see [✓E0076]", obsolete=true)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Pattern B: No replacement exists yet**
|
|
50
|
+
Write the correction first, then mark:
|
|
51
|
+
```
|
|
52
|
+
write_memory(prefix="L", content="Correct approach is XYZ\n\tDetails...") # -> L0090
|
|
53
|
+
update_memory(id="L0042", content="Superseded — see [✓L0090]", obsolete=true)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Pattern C: Just stale, no correction needed**
|
|
57
|
+
If the entry is simply outdated with no replacement (e.g., a task that's done, a project note about a past state), mark it irrelevant instead:
|
|
58
|
+
```
|
|
59
|
+
update_memory(id="T0005", content="...", irrelevant=true)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Step 4 — Consolidate duplicates
|
|
65
|
+
|
|
66
|
+
Look for entries that cover the same topic (common with P entries). Merge them:
|
|
67
|
+
|
|
68
|
+
1. Pick the **keeper** (the more complete one)
|
|
69
|
+
2. Copy unique info from the duplicate into the keeper:
|
|
70
|
+
```
|
|
71
|
+
append_memory(id="P0029", content="Session from duplicate entry\n\tDetail carried over")
|
|
72
|
+
```
|
|
73
|
+
3. Mark the duplicate irrelevant:
|
|
74
|
+
```
|
|
75
|
+
update_memory(id="P0031", content="Merged into P0029", irrelevant=true)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
You cannot delete entries yourself (only the curator can). Marking irrelevant hides them from bulk reads, which is the same practical effect.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Step 5 — Favorite audit
|
|
83
|
+
|
|
84
|
+
Check your current favorites: look for `[♥]` markers in the title listing.
|
|
85
|
+
|
|
86
|
+
- **Too many favorites?** If more than ~10% of entries are favorites, they lose their purpose. Demote the less important ones: `update_memory(id="X", content="...", favorite=false)`
|
|
87
|
+
- **Missing favorites?** Reference entries you always need (API endpoints, key architecture decisions, frequently-used patterns) should be favorited.
|
|
88
|
+
- **Sub-node favorites:** If a specific L2/L3 detail is the real reference (not the whole entry), favorite the sub-node: `update_memory(id="L0042.2", content="...", favorite=true)`
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Guidelines
|
|
93
|
+
|
|
94
|
+
- **Work in batches.** Don't try to curate 100+ entries in one session. Focus on one or two prefixes per run.
|
|
95
|
+
- **Read before marking.** Always `read_memory(id=X)` to see L2 children before marking obsolete/irrelevant. The L1 might be weak but the detail valuable.
|
|
96
|
+
- **Preserve learning value.** Error entries (E) and lessons (L) that describe *why* something failed are valuable even if the bug is fixed. Only mark obsolete if the root cause analysis is wrong.
|
|
97
|
+
- **When in doubt, skip.** You can always curate again later. False irrelevant/obsolete is harder to undo than leaving an entry alone.
|
|
98
|
+
- **Update stale L1 text.** If an entry's summary doesn't match its content anymore, rewrite it. A clear L1 is the most impactful improvement you can make.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Quick reference
|
|
103
|
+
|
|
104
|
+
| Tool | When |
|
|
105
|
+
|------|------|
|
|
106
|
+
| `read_memory(titles_only=true)` | Get full overview |
|
|
107
|
+
| `read_memory(titles_only=true, prefix="L")` | Focus on one category |
|
|
108
|
+
| `read_memory(id="L0042")` | Drill into entry before judging |
|
|
109
|
+
| `update_memory(id, content, favorite=true)` | Mark as always-show reference |
|
|
110
|
+
| `update_memory(id, content, irrelevant=true)` | Hide from bulk reads (noise) |
|
|
111
|
+
| `update_memory(id, content, obsolete=true)` | Mark as wrong (needs [✓ID]) |
|
|
112
|
+
| `append_memory(id, content)` | Merge info from duplicate |
|
|
113
|
+
| `read_memory(show_obsolete=true)` | Review already-obsolete entries |
|
|
@@ -138,10 +138,11 @@ Place `hmem.config.json` in your `HMEM_PROJECT_DIR` to customize behavior. All k
|
|
|
138
138
|
"maxLnChars": 50000,
|
|
139
139
|
"maxDepth": 5,
|
|
140
140
|
"defaultReadLimit": 100,
|
|
141
|
-
"
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
"bulkReadV2": {
|
|
142
|
+
"topAccessCount": 3,
|
|
143
|
+
"topNewestCount": 5,
|
|
144
|
+
"topObsoleteCount": 3
|
|
145
|
+
}
|
|
145
146
|
}
|
|
146
147
|
```
|
|
147
148
|
|
|
@@ -149,18 +150,7 @@ Place `hmem.config.json` in your `HMEM_PROJECT_DIR` to customize behavior. All k
|
|
|
149
150
|
- `"maxL1Chars"` + `"maxLnChars"`: set endpoints only, intermediate levels interpolated linearly
|
|
150
151
|
- `"maxCharsPerLevel"`: explicit array `[L1, L2, L3, L4, L5]`
|
|
151
152
|
|
|
152
|
-
**
|
|
153
|
-
|
|
154
|
-
Each tier is `{ "count": N, "depth": D }` — the N most recent entries get children inlined up to depth D. The highest applicable depth wins.
|
|
155
|
-
|
|
156
|
-
Default behavior:
|
|
157
|
-
| Entry position | What you see |
|
|
158
|
-
|---|---|
|
|
159
|
-
| 0–2 (most recent) | L1 + L2 + L3 |
|
|
160
|
-
| 3–9 | L1 + L2 |
|
|
161
|
-
| 10+ | L1 only |
|
|
162
|
-
|
|
163
|
-
Set to `[]` to disable (L1-only for all entries).
|
|
153
|
+
**Bulk-read tuning** (`bulkReadV2`): controls which entries get expanded (all L2 children shown) in a default `read_memory()` call. Per prefix category: top N newest + top M most-accessed are expanded. Favorites are always expanded.
|
|
164
154
|
|
|
165
155
|
---
|
|
166
156
|
|
|
@@ -19,10 +19,14 @@ If the tool `write_memory` is not available:
|
|
|
19
19
|
```
|
|
20
20
|
write_memory(
|
|
21
21
|
prefix: "E",
|
|
22
|
-
content: "
|
|
22
|
+
content: "Short Title (~50 chars)\nL1 sentence — concise, understandable without context\n\tL2 detail (1 tab)\n\t\tL3 detail (2 tabs)\n\t\t\tL4 raw data (3 tabs — rarely needed)"
|
|
23
23
|
)
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
**Title convention:** The first non-indented line is the **title** (~50 chars, configurable via `maxTitleChars` in `hmem.config.json`) — a short label for navigation, like a chapter title. The second non-indented line is the L1 summary (full sentence). If only one non-indented line is provided, the title is auto-extracted from the first `maxTitleChars` characters.
|
|
27
|
+
|
|
28
|
+
**Child node titles** are always auto-extracted from the first `maxTitleChars` characters of their content (or text before ` — ` if shorter). No explicit title needed for children.
|
|
29
|
+
|
|
26
30
|
**Indentation:** 1 tab = 1 level. Alternatively: 2 or 4 spaces per level (auto-detected).
|
|
27
31
|
**Warning:** A tab at the start of any line always means "go one level deeper" — it is structural, not content. If you need to store code or text that contains leading tabs, use spaces instead.
|
|
28
32
|
**IDs and timestamps** are assigned automatically — never write them yourself.
|
|
@@ -109,12 +113,27 @@ write_memory(
|
|
|
109
113
|
|
|
110
114
|
---
|
|
111
115
|
|
|
112
|
-
## L1 Quality
|
|
116
|
+
## Title + L1 Quality Rules
|
|
117
|
+
|
|
118
|
+
**Title:** Short navigation label, ~50 chars (configurable via `maxTitleChars`). Think "chapter title in a book".
|
|
119
|
+
- Good: `"hmem.py Performance: Bulk-Queries statt N+1"`, `"Ghost Wakeup Bug in msg-router.ts"`
|
|
120
|
+
- Bad: `"Fixed a bug"`, `"Important lesson"` (too vague)
|
|
113
121
|
|
|
114
|
-
|
|
122
|
+
**L1:** One complete, informative sentence — ~15–20 tokens.
|
|
115
123
|
- Must be understandable without any context
|
|
116
124
|
- Not "Fixed a bug" — instead "SQLite connection failed due to wrong path in .mcp.json"
|
|
117
125
|
|
|
126
|
+
**With explicit title (recommended):**
|
|
127
|
+
```
|
|
128
|
+
write_memory(prefix="L", content="hmem.py Performance\nAlle Nodes in 2 Bulk-Queries laden, nicht pro Entry einzeln\n\tload_nodes() pro Entry = N+1 SQLite-Connections")
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Without explicit title (auto-extracted):**
|
|
132
|
+
```
|
|
133
|
+
write_memory(prefix="E", content="SQLite connection failed due to wrong path in .mcp.json\n\tFix: use absolute path in env var")
|
|
134
|
+
```
|
|
135
|
+
Title auto-extracted: `"SQLite connection failed due to wrong path in .mc"`
|
|
136
|
+
|
|
118
137
|
---
|
|
119
138
|
|
|
120
139
|
## Company Knowledge (requires AL+ role)
|
|
@@ -194,9 +213,9 @@ Use when: you have new context to add without replacing what's there.
|
|
|
194
213
|
|
|
195
214
|
---
|
|
196
215
|
|
|
197
|
-
## Access Count (Automatic)
|
|
216
|
+
## Access Count (Automatic + Time-Weighted)
|
|
198
217
|
|
|
199
|
-
Access counts are managed automatically — every `read_memory` and `append_memory` call bumps the accessed entries. Entries with
|
|
218
|
+
Access counts are managed automatically — every `read_memory` and `append_memory` call bumps the accessed entries. The ranking uses **time-weighted scoring** (`access_count / log2(age_in_days + 2)`) so newer entries with fewer accesses can outrank stale old ones. Entries with the highest weighted scores get `[★]` markers and expanded treatment in bulk reads. To explicitly mark an entry as important, use `favorite: true` on `write_memory` or `update_memory`.
|
|
200
219
|
|
|
201
220
|
---
|
|
202
221
|
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: save
|
|
3
|
-
description: >
|
|
4
|
-
End-of-session save routine. Use when the user types /hmem-save or /save or asks to
|
|
5
|
-
"save", "save session", or "save progress".
|
|
6
|
-
Saves session learnings to memory via write_memory, commits git changes,
|
|
7
|
-
then compacts the conversation context.
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Save Session
|
|
11
|
-
|
|
12
|
-
Execute these steps in order. Report results after all complete.
|
|
13
|
-
|
|
14
|
-
## Step 0 — First-time setup check
|
|
15
|
-
|
|
16
|
-
Before saving, verify that hmem is ready.
|
|
17
|
-
|
|
18
|
-
**Check 1: Is write_memory available?**
|
|
19
|
-
|
|
20
|
-
Try calling `write_memory` (or check if the tool exists in your tool list).
|
|
21
|
-
|
|
22
|
-
If `write_memory` is NOT available:
|
|
23
|
-
- Tell the user: "The hmem MCP server is not connected. Run `npx hmem-mcp init` in your terminal to set it up, then restart your AI tool."
|
|
24
|
-
- **STOP. Do not continue.**
|
|
25
|
-
|
|
26
|
-
**Check 2: Does a memory file exist?**
|
|
27
|
-
|
|
28
|
-
Call `read_memory()`. If it returns entries → memory exists, skip to Step 1.
|
|
29
|
-
|
|
30
|
-
If `read_memory()` returns an **empty memory** (no entries at all) for the first time, this is likely a fresh setup. Ask the user:
|
|
31
|
-
|
|
32
|
-
> "No memory found yet. Where should hmem store your memories?
|
|
33
|
-
> 1) Global — works in any directory (`~/.hmem/`)
|
|
34
|
-
> 2) Project-local — only in this directory (current folder)
|
|
35
|
-
>
|
|
36
|
-
> Recommendation: Global for personal assistants. Project-local for team projects."
|
|
37
|
-
|
|
38
|
-
After the user chooses, walk them through the config setup:
|
|
39
|
-
|
|
40
|
-
> "Let me set up your `hmem.config.json`. I'll explain each setting — press Enter to accept the recommendation or type a new value."
|
|
41
|
-
|
|
42
|
-
Go through these parameters one by one:
|
|
43
|
-
|
|
44
|
-
| Parameter | Recommendation | Question to ask |
|
|
45
|
-
|-----------|---------------|-----------------|
|
|
46
|
-
| `maxL1Chars` | 120 | "How long should memory summaries be? (60–200 characters — shorter loads faster at startup)" |
|
|
47
|
-
| `maxDepth` | 5 | "How many detail levels do you want? (2–5 — 5 gives the most flexibility)" |
|
|
48
|
-
| `recentDepthTiers` | [{count:10,depth:2},{count:3,depth:3}] | "Auto-expand recent entries? This shows extra detail for your newest memories without extra tool calls. Recommended: yes" |
|
|
49
|
-
| `prefixes` | default | "Do you want custom memory categories beyond the defaults (P/L/E/D/M/S/T/N/H/R)? If yes, name them (e.g. R=Research, B=Bookmark). Otherwise press Enter." |
|
|
50
|
-
|
|
51
|
-
Write the resulting `hmem.config.json` to the chosen directory.
|
|
52
|
-
Tell the user: "Config saved to `<path>/hmem.config.json`. You can adjust settings anytime with `/hmem-config`."
|
|
53
|
-
|
|
54
|
-
## Step 1 — Write Memory
|
|
55
|
-
|
|
56
|
-
**IMPORTANT:** You MUST use the `write_memory` MCP tool. NEVER write directly to `.hmem` files via sqlite3 or shell commands — this bypasses WAL journaling, integrity checks, and tree logic, causing corruption or data loss.
|
|
57
|
-
|
|
58
|
-
Your L1 memory summaries are already in your context (injected at session start).
|
|
59
|
-
**Check them first** — do not re-write anything that already exists.
|
|
60
|
-
|
|
61
|
-
Only write what is **new since the last `/save` or session start**:
|
|
62
|
-
|
|
63
|
-
| Prefix | When to use |
|
|
64
|
-
|--------|-------------|
|
|
65
|
-
| `P` | (P)roject progress — work done this session |
|
|
66
|
-
| `L` | (L)essons learned applicable beyond this session |
|
|
67
|
-
| `E` | (E)rror patterns — root cause + fix |
|
|
68
|
-
| `D` | (D)ecisions — architectural or design decisions |
|
|
69
|
-
|
|
70
|
-
Quality over quantity. Skip trivial things and anything already captured.
|
|
71
|
-
|
|
72
|
-
### P entries: append to existing, don't duplicate
|
|
73
|
-
|
|
74
|
-
Before creating a new P entry, check if an existing P entry covers the **same project or topic**.
|
|
75
|
-
|
|
76
|
-
- Scan your L1 summaries (already in context) for a matching P entry.
|
|
77
|
-
- If one exists: use `append_memory` to add this session as a new L2 node.
|
|
78
|
-
- Keep the new L2 text concise (what was done this session, ~15–20 tokens).
|
|
79
|
-
- Add L3 nodes for details.
|
|
80
|
-
- If none exists: use `write_memory` to create a new P entry.
|
|
81
|
-
|
|
82
|
-
**Goal: one P entry per project, growing over time — not one P entry per session.**
|
|
83
|
-
|
|
84
|
-
```
|
|
85
|
-
# Existing entry found → append this session
|
|
86
|
-
append_memory(id="P0038", content="Session 02-24: update_memory + append_memory tools (v1.5.0)
|
|
87
|
-
update_memory: update text of single node without touching children
|
|
88
|
-
append_memory: add child nodes to existing entry, relative indentation
|
|
89
|
-
access_count auto-promotion: top-5 most accessed entries get L2 in bulk reads")
|
|
90
|
-
|
|
91
|
-
# No existing entry → create new
|
|
92
|
-
write_memory(prefix="P", content="New project: authentication service
|
|
93
|
-
Implemented JWT flow with refresh token rotation
|
|
94
|
-
Session 02-24: initial setup + basic login endpoint")
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### L / E / D entries: always create new
|
|
98
|
-
|
|
99
|
-
For lessons, errors, and decisions — always create a new entry. These are indexed insights, not project logs.
|
|
100
|
-
|
|
101
|
-
```
|
|
102
|
-
write_memory(prefix="L", content="Always restart MCP server after recompiling TypeScript
|
|
103
|
-
Running process holds the old dist — tool calls return stale results otherwise")
|
|
104
|
-
|
|
105
|
-
write_memory(prefix="E", content="write_memory returned HMEM_PROJECT_DIR not set
|
|
106
|
-
Cause: relative path in .mcp.json env — must be absolute
|
|
107
|
-
Fix: replace with full absolute path, restart AI tool")
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
## Step 2 — Commit & Push
|
|
111
|
-
|
|
112
|
-
**IMPORTANT:** Step 1 (write_memory) MUST complete before this step. The `.hmem` file is modified by write_memory — if you push before writing, the memory changes won't be included in the commit.
|
|
113
|
-
|
|
114
|
-
If in a git repository:
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
git add -A
|
|
118
|
-
git commit -m "concise imperative summary of this session's changes"
|
|
119
|
-
git push
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
Skip if there are no changes or no git repo.
|
|
123
|
-
|
|
124
|
-
## Step 3 — Compact (Claude only)
|
|
125
|
-
|
|
126
|
-
If you are running on **Claude** (Claude Code, claude.ai): run `/compact` to compress the conversation context. The next interaction starts fresh with your updated memories already loaded.
|
|
127
|
-
|
|
128
|
-
If you are running on **Gemini CLI, OpenCode, or another tool**: skip this step — `/compact` is a Claude-specific command.
|