@hir4ta/mneme 0.24.1 → 0.24.3

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mneme",
3
3
  "description": "A plugin that provides long-term memory for Claude Code. It automatically saves context lost during auto-compact, offering features for session restoration, recording technical decisions, and learning developer patterns.",
4
- "version": "0.24.1",
4
+ "version": "0.24.3",
5
5
  "author": {
6
6
  "name": "hir4ta"
7
7
  },
package/README.ja.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # mneme
2
2
 
3
- ![Version](https://img.shields.io/badge/version-0.24.1-blue)
3
+ ![Version](https://img.shields.io/badge/version-0.24.3-blue)
4
4
  ![Node.js](https://img.shields.io/badge/node-%3E%3D22.5.0-brightgreen)
5
5
  [![NPM Version](https://img.shields.io/npm/v/%40hir4ta%2Fmneme)](https://www.npmjs.com/package/@hir4ta/mneme)
6
6
  [![MIT License](https://img.shields.io/npm/l/%40hir4ta%2Fmneme)](https://github.com/hir4ta/mneme/blob/main/LICENSE)
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # mneme
2
2
 
3
- ![Version](https://img.shields.io/badge/version-0.24.1-blue)
3
+ ![Version](https://img.shields.io/badge/version-0.24.3-blue)
4
4
  ![Node.js](https://img.shields.io/badge/node-%3E%3D22.5.0-brightgreen)
5
5
  [![NPM Version](https://img.shields.io/npm/v/%40hir4ta%2Fmneme)](https://www.npmjs.com/package/@hir4ta/mneme)
6
6
  [![MIT License](https://img.shields.io/npm/l/%40hir4ta%2Fmneme)](https://github.com/hir4ta/mneme/blob/main/LICENSE)
package/dist/server.js CHANGED
@@ -4493,7 +4493,7 @@ misc.get("/project", (c) => {
4493
4493
  }
4494
4494
  } catch {
4495
4495
  }
4496
- const version = "0.24.1";
4496
+ const version = "0.24.3";
4497
4497
  return c.json({
4498
4498
  name: projectName,
4499
4499
  path: projectRoot,
@@ -4,8 +4,9 @@
4
4
  #
5
5
  # Purpose: Search mneme for relevant context and approved rules,
6
6
  # inject as additionalContext using shared search-core logic.
7
+ # Also injects Claude Session ID (workaround for anthropics/claude-code#16538).
7
8
  #
8
- # Input (stdin): JSON with prompt, cwd
9
+ # Input (stdin): JSON with prompt, session_id, cwd
9
10
  # Output (stdout): JSON with hookSpecificOutput.additionalContext (if matches found)
10
11
  #
11
12
 
@@ -23,13 +24,16 @@ fi
23
24
 
24
25
  prompt=$(echo "$input_json" | jq -r ".prompt // empty" 2>/dev/null || echo "")
25
26
  cwd=$(echo "$input_json" | jq -r ".cwd // empty" 2>/dev/null || echo "")
27
+ session_id=$(echo "$input_json" | jq -r ".session_id // empty" 2>/dev/null || echo "")
26
28
 
29
+ # Determine if search should run (session ID is always injected regardless)
30
+ run_search=true
27
31
  if [ -z "$prompt" ] || [ ${#prompt} -lt 10 ]; then
28
- exit 0
32
+ run_search=false
29
33
  fi
30
34
 
31
35
  if [[ "$prompt" == /mneme* ]]; then
32
- exit 0
36
+ run_search=false
33
37
  fi
34
38
 
35
39
  if [ -z "$cwd" ]; then
@@ -37,104 +41,114 @@ if [ -z "$cwd" ]; then
37
41
  fi
38
42
  cwd=$(cd "$cwd" 2>/dev/null && pwd || echo "$cwd")
39
43
 
40
- # Cap very large prompts to avoid overly expensive search
41
- if [ ${#prompt} -gt 4000 ]; then
42
- prompt="${prompt:0:4000}"
43
- fi
44
-
45
44
  if ! validate_mneme "$cwd"; then
46
45
  exit 0
47
46
  fi
48
47
 
49
- PLUGIN_ROOT="$(get_plugin_root)"
50
-
51
- search_script=$(find_script "$PLUGIN_ROOT" "prompt-search")
52
- if [ -z "$search_script" ]; then
53
- exit 0
54
- fi
55
-
56
- # Detect changed files for file-based session recommendation
57
- changed_files=""
58
- if command -v git >/dev/null 2>&1 && git -C "$cwd" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
59
- changed_files=$(cd "$cwd" && {
60
- git diff --name-only HEAD 2>/dev/null
61
- git diff --name-only --cached 2>/dev/null
62
- } | sort -u | head -20 | paste -sd "," - 2>/dev/null || echo "")
63
- fi
64
-
65
- search_output=$(invoke_node "$search_script" \
66
- --query "$prompt" --project "$cwd" --limit 5 \
67
- ${changed_files:+--files "$changed_files"} 2>/dev/null || echo "")
48
+ # --- Search (skipped for short prompts and /mneme commands) ---
49
+ context_message=""
50
+ rules_message=""
68
51
 
69
- if [ -z "$search_output" ]; then
70
- exit 0
71
- fi
52
+ if [ "$run_search" = true ]; then
53
+ # Cap very large prompts to avoid overly expensive search
54
+ if [ ${#prompt} -gt 4000 ]; then
55
+ prompt="${prompt:0:4000}"
56
+ fi
72
57
 
73
- success=$(echo "$search_output" | jq -r ".success // false" 2>/dev/null || echo "false")
74
- if [ "$success" != "true" ]; then
75
- exit 0
76
- fi
58
+ PLUGIN_ROOT="$(get_plugin_root)"
59
+
60
+ search_script=$(find_script "$PLUGIN_ROOT" "prompt-search")
61
+ if [ -n "$search_script" ]; then
62
+ # Detect changed files for file-based session recommendation
63
+ changed_files=""
64
+ if command -v git >/dev/null 2>&1 && git -C "$cwd" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
65
+ changed_files=$(cd "$cwd" && {
66
+ git diff --name-only HEAD 2>/dev/null
67
+ git diff --name-only --cached 2>/dev/null
68
+ } | sort -u | head -20 | paste -sd "," - 2>/dev/null || echo "")
69
+ fi
77
70
 
78
- # Format session/interaction context (existing behavior)
79
- context_message=""
80
- context_lines=$(echo "$search_output" | jq -r '
81
- .results
82
- | map(select(.score >= 3 and (.type == "session" or .type == "unit")))
83
- | .[:3]
84
- | map("[\(.type):\(.id)] \(.title) | match: \((.matchedFields // []) | join(","))")
85
- | join("\n")
86
- ')
87
-
88
- # Format file-based session recommendations
89
- file_rec_lines=$(echo "$search_output" | jq -r '
90
- .fileRecommendations // []
91
- | .[:3]
92
- | map("[session:\(.sessionId)] \(.title) | files: \(.matchedFiles | join(", "))")
93
- | join("\n")
94
- ')
95
-
96
- if [ -n "$context_lines" ] && [ "$context_lines" != "null" ] || \
97
- [ -n "$file_rec_lines" ] && [ "$file_rec_lines" != "null" ]; then
98
- context_parts=""
99
- if [ -n "$context_lines" ] && [ "$context_lines" != "null" ]; then
100
- context_parts="Related context found (sessions/units):
71
+ search_output=$(invoke_node "$search_script" \
72
+ --query "$prompt" --project "$cwd" --limit 5 \
73
+ ${changed_files:+--files "$changed_files"} 2>/dev/null || echo "")
74
+
75
+ if [ -n "$search_output" ]; then
76
+ success=$(echo "$search_output" | jq -r ".success // false" 2>/dev/null || echo "false")
77
+ if [ "$success" = "true" ]; then
78
+ # Format session/interaction context
79
+ context_lines=$(echo "$search_output" | jq -r '
80
+ .results
81
+ | map(select(.score >= 3 and (.type == "session" or .type == "unit")))
82
+ | .[:3]
83
+ | map("[\(.type):\(.id)] \(.title) | match: \((.matchedFields // []) | join(","))")
84
+ | join("\n")
85
+ ')
86
+
87
+ # Format file-based session recommendations
88
+ file_rec_lines=$(echo "$search_output" | jq -r '
89
+ .fileRecommendations // []
90
+ | .[:3]
91
+ | map("[session:\(.sessionId)] \(.title) | files: \(.matchedFiles | join(", "))")
92
+ | join("\n")
93
+ ')
94
+
95
+ if [ -n "$context_lines" ] && [ "$context_lines" != "null" ] || \
96
+ [ -n "$file_rec_lines" ] && [ "$file_rec_lines" != "null" ]; then
97
+ context_parts=""
98
+ if [ -n "$context_lines" ] && [ "$context_lines" != "null" ]; then
99
+ context_parts="Related context found (sessions/units):
101
100
  ${context_lines}"
102
- fi
103
- if [ -n "$file_rec_lines" ] && [ "$file_rec_lines" != "null" ]; then
104
- if [ -n "$context_parts" ]; then
105
- context_parts="${context_parts}
101
+ fi
102
+ if [ -n "$file_rec_lines" ] && [ "$file_rec_lines" != "null" ]; then
103
+ if [ -n "$context_parts" ]; then
104
+ context_parts="${context_parts}
106
105
  "
107
- fi
108
- context_parts="${context_parts}Related sessions (editing same files):
106
+ fi
107
+ context_parts="${context_parts}Related sessions (editing same files):
109
108
  ${file_rec_lines}"
110
- fi
111
- context_message="<mneme-context>
109
+ fi
110
+ context_message="<mneme-context>
112
111
  ${context_parts}
113
112
  To explore deeper: use /mneme:search with specific technical terms, error messages, or file paths.
114
113
  </mneme-context>"
115
- fi
116
-
117
- # Format approved rules
118
- rules_message=""
119
- rules_lines=$(echo "$search_output" | jq -r '
120
- .rules // []
121
- | map(select(.score >= 2))
122
- | .[:5]
123
- | map("[\(.sourceType):\(.id)] (\(.priority // "—")) \(.text)")
124
- | join("\n")
125
- ')
126
-
127
- if [ -n "$rules_lines" ] && [ "$rules_lines" != "null" ]; then
128
- rules_message="<mneme-rules>
114
+ fi
115
+
116
+ # Format approved rules
117
+ rules_lines=$(echo "$search_output" | jq -r '
118
+ .rules // []
119
+ | map(select(.score >= 2))
120
+ | .[:5]
121
+ | map("[\(.sourceType):\(.id)] (\(.priority // "—")) \(.text)")
122
+ | join("\n")
123
+ ')
124
+
125
+ if [ -n "$rules_lines" ] && [ "$rules_lines" != "null" ]; then
126
+ rules_message="<mneme-rules>
129
127
  Approved development rules (apply during this response):
130
128
  ${rules_lines}
131
129
  </mneme-rules>"
130
+ fi
131
+ fi
132
+ fi
133
+ fi
132
134
  fi
133
135
 
134
- # Combine both sections
136
+ # --- Assemble full context ---
135
137
  full_context=""
138
+
139
+ # Session ID injection (workaround for anthropics/claude-code#16538:
140
+ # Plugin SessionStart hooks don't surface additionalContext to Claude)
141
+ if [ -n "$session_id" ]; then
142
+ full_context="**Claude Session ID:** ${session_id}"
143
+ fi
144
+
136
145
  if [ -n "$context_message" ]; then
137
- full_context="$context_message"
146
+ if [ -n "$full_context" ]; then
147
+ full_context="${full_context}
148
+ ${context_message}"
149
+ else
150
+ full_context="$context_message"
151
+ fi
138
152
  fi
139
153
  if [ -n "$rules_message" ]; then
140
154
  if [ -n "$full_context" ]; then
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hir4ta/mneme",
3
- "version": "0.24.1",
3
+ "version": "0.24.3",
4
4
  "description": "Long-term memory plugin for Claude Code - automated session saving, recording technical decisions, and web dashboard",
5
5
  "keywords": [
6
6
  "claude",
@@ -5,7 +5,7 @@ description: |
5
5
  Use when: (1) finishing meaningful implementation work, (2) capturing reusable guidance,
6
6
  (3) before ending a long session.
7
7
  disable-model-invocation: false
8
- allowed-tools: Read, Write, Edit, Glob, Grep, Bash, mcp__mneme-db__mneme_save_interactions, mcp__mneme-db__mneme_update_session_summary, mcp__mneme-db__mneme_mark_session_committed, mcp__mneme-db__mneme_rule_linter, mcp__mneme-db__mneme_search_eval
8
+ allowed-tools: Read, Write, Edit, Glob, Grep, Bash, mcp__mneme-db__mneme_save_interactions, mcp__mneme-db__mneme_update_session_summary, mcp__mneme-db__mneme_mark_session_committed, mcp__mneme-db__mneme_validate_sources, mcp__mneme-db__mneme_list_sessions, mcp__mneme-db__mneme_rule_linter, mcp__mneme-db__mneme_search_eval
9
9
  ---
10
10
 
11
11
  # /mneme:save
@@ -43,20 +43,40 @@ Always render missing required fields as blocking errors before write.
43
43
 
44
44
  ## Session ID resolution
45
45
 
46
+ **STOP. Before doing ANYTHING else, find the Claude Session ID.**
47
+
48
+ The UserPromptSubmit hook injects the session ID into every prompt's context. Look for this exact line in the system-reminder messages above:
49
+
50
+ ```
51
+ **Claude Session ID:** <UUID>
52
+ ```
53
+
54
+ Copy the full UUID value (36 characters, e.g. `a1b2c3d4-e5f6-7890-abcd-ef1234567890`).
55
+
46
56
  <required>
47
- - Get the full Claude Session ID from the SessionStart context injected at the top of this conversation (look for `**Claude Session ID:**`)
48
- - Do NOT run any Bash commands to discover the session ID
49
- - NEVER run exploratory commands like `printenv`, `find`, `echo $MNEME_SESSION_ID`, or `ls -t ~/.claude/projects/*/`
57
+ - The Claude Session ID is ALREADY in this conversation — injected by the UserPromptSubmit hook on every prompt
58
+ - Search for the literal text `**Claude Session ID:**` in system-reminder messages above
59
+ - Copy the full 36-character UUID that follows it
60
+ - Do NOT run Bash, Glob, Grep, or any other tool to discover the session ID
61
+ - Do NOT call `mneme_list_sessions` to find it (that is only for the fallback below)
62
+ - Do NOT run `printenv`, `find`, `echo`, `ls`, `stat`, or any exploratory commands
50
63
  </required>
51
64
 
65
+ **Fallback (only if `**Claude Session ID:**` is genuinely not found in the conversation):**
66
+ 1. Call `mneme_list_sessions` with the current project path and `limit: 1` to get the most recent session
67
+ 2. Use that session's `sessionId` as the Claude Session ID
68
+ 3. If that also fails, ask the user for the session ID
69
+
52
70
  ## Execution phases
53
71
 
54
- 1. **Master session merge**
55
- - Merge linked/resumed child sessions into the master session.
72
+ 1. **Master session merge (only if resumed)**
73
+ - Check if this is a resumed session: look for `(Resumed)` in the `**Session:**` line of the SessionStart context
74
+ - **If NOT resumed**: skip this phase (fresh session, no merge needed)
75
+ - **If resumed**: the `mneme_save_interactions` tool automatically resolves session-links internally, so just pass the current `claudeSessionId` — it saves to the correct master session
76
+ - No manual action needed in most cases; this phase exists as a checkpoint
56
77
 
57
78
  2. **Interactions commit**
58
79
  - Save transcript interactions to `.mneme/local.db` via `mneme_save_interactions`.
59
- - Do NOT call `mneme_mark_session_committed` yet (wait until after Phase 3).
60
80
 
61
81
  3. **Session summary extraction (required MCP)**
62
82
  - Extract from the conversation: `title`, `goal`, `outcome`, `description`, `tags`, `sessionType`.
@@ -70,12 +90,12 @@ Always render missing required fields as blocking errors before write.
70
90
  - `handoff`: continuation info (stoppedReason, notes, nextSteps)
71
91
  - `references`: referenced document URLs and file paths (type, url, path, title, description)
72
92
  - **MUST call `mneme_update_session_summary`** MCP tool with all extracted data.
73
- This writes the summary to `.mneme/sessions/` JSON file, ensuring the session is preserved on SessionEnd.
74
- - **Then call `mneme_mark_session_committed`** to finalize the commit.
93
+ This writes the summary to `.mneme/sessions/` JSON file and **automatically marks the session as committed** (no separate `mneme_mark_session_committed` call needed).
75
94
 
76
95
  <required>
77
96
  - Call `mneme_update_session_summary` with: `claudeSessionId`, `title`, `summary` (`goal`, `outcome`), `tags`, `sessionType`, `filesModified`, `technologies`
78
- - Call `mneme_mark_session_committed` AFTER `mneme_update_session_summary` succeeds
97
+ - Do NOT call `mneme_mark_session_committed` separately — it is called internally by `mneme_update_session_summary`
98
+ - Only call `mneme_mark_session_committed` explicitly if `mneme_update_session_summary` failed and you need error recovery
79
99
  - Do NOT skip this step even for short/research sessions
80
100
  </required>
81
101
 
@@ -117,6 +137,18 @@ The following data is easily lost in summaries, so save it explicitly as structu
117
137
  - Official documentation URLs confirmed via WebFetch/WebSearch
118
138
  - Important file paths referenced
119
139
 
140
+ ### Pre-read: Load valid tags before extraction (Phases 4-6)
141
+
142
+ Before writing any decisions, patterns, or rules, read `.mneme/tags.json` to get the list of valid tag IDs.
143
+
144
+ <required>
145
+ - Read `.mneme/tags.json` and extract all `id` values from the `tags` array
146
+ - Only use tag IDs that exist in this file — do NOT invent new tag IDs
147
+ - If no existing tag fits perfectly, use the closest match from the list
148
+ - Common tag IDs: `frontend`, `backend`, `api`, `db`, `infra`, `feature`, `bugfix`, `refactor`, `test`, `docs`, `config`, `perf`, `security`, `architecture`, `auth`, `cache`, `search`, `batch`, `integration`, `mcp`, `llm`, `error-handling`, `type`, `ui`
149
+ - The full list has 66+ tags across categories: domain, phase, infra, cloud, architecture, feature, ui, data, ai, quality, workflow
150
+ </required>
151
+
120
152
  4. **Decision extraction (source)**
121
153
  - Persist concrete choices and rationale to `decisions/YYYY/MM/*.json`.
122
154
  - Apply the classification matrix: only extract items that are **one-time choices with context-specific reasoning**.
@@ -132,6 +164,7 @@ Decision content quality:
132
164
  - BAD: `["Use HS256"]`
133
165
  - GOOD: `["Use HS256 -> rejected: key rotation difficult in production"]`
134
166
  If alternatives is a string array, append " -> rejected: reason" to each entry.
167
+ - `tags`: MUST use only IDs from `.mneme/tags.json` (loaded in pre-read step above)
135
168
  </required>
136
169
 
137
170
  5. **Pattern extraction (source)**
@@ -150,6 +183,11 @@ Pattern content quality:
150
183
  - BAD: "when impact is limited"
151
184
  - GOOD: "when call sites are 5 or fewer and the interface is stable"
152
185
  - **Expected outcomes**: concrete effects when this pattern is applied
186
+ - `type`: MUST be one of: `"good"`, `"bad"`, `"error-solution"`
187
+ - `"good"`: best practice to follow
188
+ - `"bad"`: anti-pattern to avoid
189
+ - `"error-solution"`: additionally requires `errorPattern` and `solution` fields
190
+ - `tags`: MUST use only IDs from `.mneme/tags.json` (loaded in pre-read step above)
153
191
  </required>
154
192
 
155
193
  6. **Rule extraction (source)**
@@ -170,6 +208,7 @@ Rule content quality:
170
208
  - `category`: rule classification (type-safety, testing, security, performance, etc.)
171
209
  - Active rule must include: `id`, `key`, `text`, `category`, `tags`, `priority`, `rationale`
172
210
  - `priority` must be one of: `p0`, `p1`, `p2`
211
+ - `tags`: MUST use only IDs from `.mneme/tags.json` (loaded in pre-read step above)
173
212
  </required>
174
213
 
175
214
  7. **Development rule candidates report**
@@ -178,9 +217,10 @@ Rule content quality:
178
217
  - Only items approved by the engineer in the dashboard become active development rules.
179
218
  - Do not perform inline approval.
180
219
 
181
- 8. **Auto quality checks (required MCP)**
182
- - Run `mneme_rule_linter` (`ruleType: "all"`).
183
- - Run `mneme_search_eval` (`mode: "run"`).
220
+ 8. **Auto quality checks (required MCP, run in parallel)**
221
+ - Run these two tools **in parallel** (they are independent, no dependency between them):
222
+ - `mneme_rule_linter` (`ruleType: "all"`)
223
+ - `mneme_search_eval` (`mode: "run"`)
184
224
 
185
225
  ## Source definitions and exclusivity (must follow)
186
226