@hir4ta/mneme 0.17.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.
Files changed (43) hide show
  1. package/.claude-plugin/plugin.json +29 -0
  2. package/.mcp.json +18 -0
  3. package/README.ja.md +400 -0
  4. package/README.md +410 -0
  5. package/bin/mneme.js +203 -0
  6. package/dist/lib/db.js +340 -0
  7. package/dist/lib/fuzzy-search.js +214 -0
  8. package/dist/lib/github.js +121 -0
  9. package/dist/lib/similarity.js +193 -0
  10. package/dist/lib/utils.js +62 -0
  11. package/dist/public/apple-touch-icon.png +0 -0
  12. package/dist/public/assets/index-BgqCALAg.css +1 -0
  13. package/dist/public/assets/index-EMvn4VEa.js +330 -0
  14. package/dist/public/assets/react-force-graph-2d-DWoBaKmT.js +46 -0
  15. package/dist/public/favicon-128-max.png +0 -0
  16. package/dist/public/favicon-256-max.png +0 -0
  17. package/dist/public/favicon-32-max.png +0 -0
  18. package/dist/public/favicon-512-max.png +0 -0
  19. package/dist/public/favicon-64-max.png +0 -0
  20. package/dist/public/index.html +15 -0
  21. package/dist/server.js +4791 -0
  22. package/dist/servers/db-server.js +30558 -0
  23. package/dist/servers/search-server.js +30366 -0
  24. package/hooks/default-tags.json +1055 -0
  25. package/hooks/hooks.json +61 -0
  26. package/hooks/post-tool-use.sh +96 -0
  27. package/hooks/pre-compact.sh +187 -0
  28. package/hooks/session-end.sh +567 -0
  29. package/hooks/session-start.sh +380 -0
  30. package/hooks/user-prompt-submit.sh +253 -0
  31. package/package.json +77 -0
  32. package/servers/db-server.ts +993 -0
  33. package/servers/search-server.ts +675 -0
  34. package/skills/AGENTS.override.md +5 -0
  35. package/skills/harvest/skill.md +295 -0
  36. package/skills/init-mneme/skill.md +101 -0
  37. package/skills/plan/skill.md +422 -0
  38. package/skills/report/skill.md +74 -0
  39. package/skills/resume/skill.md +278 -0
  40. package/skills/review/skill.md +419 -0
  41. package/skills/save/skill.md +482 -0
  42. package/skills/search/skill.md +175 -0
  43. package/skills/using-mneme/skill.md +185 -0
@@ -0,0 +1,61 @@
1
+ {
2
+ "description": "mneme plugin hooks for session management, auto-save, and memory search",
3
+ "hooks": {
4
+ "UserPromptSubmit": [
5
+ {
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/user-prompt-submit.sh",
10
+ "timeout": 10
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "SessionStart": [
16
+ {
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh",
21
+ "timeout": 15
22
+ }
23
+ ]
24
+ }
25
+ ],
26
+ "PreCompact": [
27
+ {
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre-compact.sh",
32
+ "timeout": 30
33
+ }
34
+ ]
35
+ }
36
+ ],
37
+ "SessionEnd": [
38
+ {
39
+ "hooks": [
40
+ {
41
+ "type": "command",
42
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/session-end.sh",
43
+ "timeout": 30
44
+ }
45
+ ]
46
+ }
47
+ ],
48
+ "PostToolUse": [
49
+ {
50
+ "matcher": "Bash",
51
+ "hooks": [
52
+ {
53
+ "type": "command",
54
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/post-tool-use.sh",
55
+ "timeout": 10
56
+ }
57
+ ]
58
+ }
59
+ ]
60
+ }
61
+ }
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # post-tool-use.sh - Post-tool use hook for Bash operations
4
+ #
5
+ # This hook is called after Bash tool execution.
6
+ # It detects errors, searches for matching patterns in mneme,
7
+ # and suggests solutions from past error-solution patterns.
8
+ #
9
+ # Input (stdin): JSON with tool_name, tool_input, tool_response, cwd
10
+ # Output (stdout): JSON with {"continue": true} and optional additionalContext
11
+ # Exit codes: 0 = success (non-blocking)
12
+ #
13
+ # Dependencies: jq
14
+
15
+ set -euo pipefail
16
+
17
+ # Read stdin
18
+ input_json=$(cat)
19
+
20
+ # Extract relevant fields (tool_response is the official field name)
21
+ exit_code=$(echo "$input_json" | jq -r '.tool_response.exit_code // .tool_result.exit_code // 0')
22
+ stderr=$(echo "$input_json" | jq -r '.tool_response.stderr // .tool_result.stderr // ""')
23
+ command=$(echo "$input_json" | jq -r '.tool_input.command // ""')
24
+ cwd=$(echo "$input_json" | jq -r '.cwd // empty')
25
+
26
+ # If no cwd, use PWD
27
+ if [ -z "$cwd" ]; then
28
+ cwd="${PWD}"
29
+ fi
30
+
31
+ # Check for error conditions
32
+ if [[ "$exit_code" != "0" && -n "$stderr" ]]; then
33
+ # Error detected - search for matching patterns first
34
+
35
+ patterns_dir="${cwd}/.mneme/patterns"
36
+ matched_solution=""
37
+ matched_pattern=""
38
+ matched_reasoning=""
39
+
40
+ # Search patterns if directory exists
41
+ if [ -d "$patterns_dir" ]; then
42
+ # Get first 500 chars of stderr for matching
43
+ stderr_sample=$(echo "$stderr" | head -c 500)
44
+
45
+ # Search through all pattern files
46
+ for pattern_file in "$patterns_dir"/*.json; do
47
+ [ -f "$pattern_file" ] || continue
48
+
49
+ # Extract error-solution patterns and check for matches (tab-separated output)
50
+ match_result=$(jq -r --arg stderr "$stderr_sample" '
51
+ [.patterns // [] | .[] | select(type == "object" and .type == "error-solution")] as $patterns
52
+ | [$patterns[] |
53
+ . as $p |
54
+ (if ($p.errorRegex // "" | length > 0) and ($stderr | test($p.errorRegex; "i")) then true
55
+ elif ($p.errorPattern // "" | length > 0) and ($stderr | contains($p.errorPattern)) then true
56
+ else false end) as $matched |
57
+ select($matched)
58
+ ]
59
+ | if length > 0 then
60
+ .[0] | "MATCH\t" + (.errorPattern // "unknown") + "\t" + (.solution // "no solution") + "\t" + (.reasoning // "")
61
+ else
62
+ empty
63
+ end
64
+ ' "$pattern_file" 2>/dev/null || echo "")
65
+
66
+ if [[ "$match_result" == MATCH$'\t'* ]]; then
67
+ # Parse the tab-separated match result
68
+ matched_pattern=$(echo "$match_result" | cut -f2)
69
+ matched_solution=$(echo "$match_result" | cut -f3)
70
+ matched_reasoning=$(echo "$match_result" | cut -f4)
71
+ break
72
+ fi
73
+ done
74
+ fi
75
+
76
+ # Build suggestion message
77
+ if [ -n "$matched_solution" ]; then
78
+ # Pattern match found - suggest the solution
79
+ suggestion="**Past solution found:**\\n"
80
+ suggestion+="Error pattern: ${matched_pattern}\\n"
81
+ suggestion+="Solution: ${matched_solution}\\n"
82
+ if [ -n "$matched_reasoning" ]; then
83
+ suggestion+="Reasoning: ${matched_reasoning}\\n"
84
+ fi
85
+ suggestion+="\\nApply this solution?"
86
+ else
87
+ # No match - just note the error
88
+ suggestion="Error detected (exit code: $exit_code). No matching pattern found in mneme."
89
+ fi
90
+
91
+ # Output with additionalContext
92
+ printf '{"continue": true, "additionalContext": "%s"}\n' "$suggestion"
93
+ else
94
+ # No error - continue normally
95
+ echo '{"continue": true}'
96
+ fi
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # pre-compact.sh - Backup interactions before Auto-Compact
4
+ #
5
+ # Saves current interactions to project-local SQLite (.mneme/local.db)
6
+ # pre_compact_backups table before context is compressed.
7
+ # Does NOT create summary - summary creation is manual via /mneme:save.
8
+ #
9
+ # Input (stdin): JSON with session_id, transcript_path, cwd, trigger
10
+ # Output (stdout): JSON with {"continue": true}
11
+ # Exit codes: 0 = success (non-blocking, always continues)
12
+ #
13
+ # Dependencies: jq, sqlite3
14
+
15
+ set -euo pipefail
16
+
17
+ # Read stdin
18
+ input_json=$(cat)
19
+
20
+ # Extract fields
21
+ session_id=$(echo "$input_json" | jq -r '.session_id // empty')
22
+ transcript_path=$(echo "$input_json" | jq -r '.transcript_path // empty')
23
+ cwd=$(echo "$input_json" | jq -r '.cwd // empty')
24
+
25
+ # If no cwd, use PWD
26
+ if [ -z "$cwd" ]; then
27
+ cwd="${PWD}"
28
+ fi
29
+
30
+ # Find session file
31
+ if [ -z "$session_id" ]; then
32
+ echo '{"continue": true}'
33
+ exit 0
34
+ fi
35
+
36
+ session_short_id="${session_id:0:8}"
37
+ mneme_dir="${cwd}/.mneme"
38
+ sessions_dir="${mneme_dir}/sessions"
39
+ session_links_dir="${mneme_dir}/session-links"
40
+
41
+ # Local database path (project-local)
42
+ db_path="${mneme_dir}/local.db"
43
+
44
+ # The session ID to use for storing data (may be updated to master session ID)
45
+ mneme_session_id="$session_short_id"
46
+
47
+ # First, try to find session file with current session ID
48
+ session_file=$(find "$sessions_dir" -name "${session_short_id}.json" -type f 2>/dev/null | head -1)
49
+
50
+ # If not found, check session-links for master session ID
51
+ if [ -z "$session_file" ] || [ ! -f "$session_file" ]; then
52
+ session_link_file="${session_links_dir}/${session_short_id}.json"
53
+ if [ -f "$session_link_file" ]; then
54
+ master_session_id=$(jq -r '.masterSessionId // empty' "$session_link_file" 2>/dev/null || echo "")
55
+ if [ -n "$master_session_id" ]; then
56
+ session_file=$(find "$sessions_dir" -type f -name "${master_session_id}.json" 2>/dev/null | head -1)
57
+ if [ -n "$session_file" ] && [ -f "$session_file" ]; then
58
+ mneme_session_id="$master_session_id"
59
+ echo "[mneme] PreCompact: Using master session via session-link: ${master_session_id}" >&2
60
+ fi
61
+ fi
62
+ fi
63
+ fi
64
+
65
+ if [ -z "$session_file" ] || [ ! -f "$session_file" ]; then
66
+ echo '{"continue": true}'
67
+ exit 0
68
+ fi
69
+
70
+ # Get git user for owner field
71
+ owner=$(git -C "$cwd" config user.name 2>/dev/null || whoami || echo "unknown")
72
+
73
+ # Escape project path for SQL
74
+ project_path_escaped="${cwd//\'/\'\'}"
75
+
76
+ # Determine plugin root directory for schema
77
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
78
+ PLUGIN_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
79
+ schema_path="${PLUGIN_ROOT}/lib/schema.sql"
80
+
81
+ # Initialize local SQLite database if not exists
82
+ init_database() {
83
+ if [ ! -d "$mneme_dir" ]; then
84
+ mkdir -p "$mneme_dir"
85
+ fi
86
+ if [ ! -f "$db_path" ]; then
87
+ if [ -f "$schema_path" ]; then
88
+ sqlite3 "$db_path" < "$schema_path"
89
+ echo "[mneme] Local SQLite database initialized: ${db_path}" >&2
90
+ else
91
+ # Minimal schema if schema.sql not found
92
+ sqlite3 "$db_path" <<'SQLEOF'
93
+ CREATE TABLE IF NOT EXISTS interactions (
94
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
95
+ session_id TEXT NOT NULL,
96
+ project_path TEXT NOT NULL,
97
+ repository TEXT,
98
+ repository_url TEXT,
99
+ repository_root TEXT,
100
+ owner TEXT NOT NULL,
101
+ role TEXT NOT NULL,
102
+ content TEXT NOT NULL,
103
+ thinking TEXT,
104
+ tool_calls TEXT,
105
+ timestamp TEXT NOT NULL,
106
+ is_compact_summary INTEGER DEFAULT 0,
107
+ created_at TEXT DEFAULT (datetime('now'))
108
+ );
109
+ CREATE INDEX IF NOT EXISTS idx_interactions_session ON interactions(session_id);
110
+ CREATE INDEX IF NOT EXISTS idx_interactions_owner ON interactions(owner);
111
+ CREATE INDEX IF NOT EXISTS idx_interactions_project ON interactions(project_path);
112
+
113
+ CREATE TABLE IF NOT EXISTS pre_compact_backups (
114
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
115
+ session_id TEXT NOT NULL,
116
+ project_path TEXT NOT NULL,
117
+ owner TEXT NOT NULL,
118
+ interactions TEXT NOT NULL,
119
+ created_at TEXT DEFAULT (datetime('now'))
120
+ );
121
+ CREATE INDEX IF NOT EXISTS idx_backups_session ON pre_compact_backups(session_id);
122
+ CREATE INDEX IF NOT EXISTS idx_backups_project ON pre_compact_backups(project_path);
123
+ SQLEOF
124
+ fi
125
+ fi
126
+ # Configure pragmas
127
+ sqlite3 "$db_path" "PRAGMA journal_mode = WAL; PRAGMA busy_timeout = 5000; PRAGMA synchronous = NORMAL;" 2>/dev/null || true
128
+ }
129
+
130
+ echo "[mneme] PreCompact: Backing up interactions before Auto-Compact..." >&2
131
+
132
+ # Extract current interactions from transcript (same logic as session-end.sh)
133
+ if [ -n "$transcript_path" ] && [ -f "$transcript_path" ]; then
134
+ # Initialize database
135
+ init_database
136
+
137
+ interactions_json=$(cat "$transcript_path" | jq -s '
138
+ # User messages (text only, exclude tool results)
139
+ [.[] | select(.type == "user" and .message.role == "user" and (.message.content | type) == "string") | {
140
+ timestamp: .timestamp,
141
+ content: .message.content
142
+ }] as $user_messages |
143
+
144
+ # All assistant messages with thinking or text
145
+ [.[] | select(.type == "assistant") | . as $msg |
146
+ ($msg.message.content // []) |
147
+ {
148
+ timestamp: $msg.timestamp,
149
+ thinking: ([.[] | select(.type == "thinking") | .thinking] | join("\n")),
150
+ text: ([.[] | select(.type == "text") | .text] | join("\n"))
151
+ } | select(.thinking != "" or .text != "")
152
+ ] as $all_assistant |
153
+
154
+ # Build interactions by grouping all assistant responses between user messages
155
+ [range(0; $user_messages | length) | . as $i |
156
+ $user_messages[$i] as $user |
157
+ # Get next user message timestamp (or far future if last)
158
+ (if $i + 1 < ($user_messages | length) then $user_messages[$i + 1].timestamp else "9999-12-31T23:59:59Z" end) as $next_user_ts |
159
+ # Collect all assistant responses between this user message and next
160
+ [$all_assistant[] | select(.timestamp > $user.timestamp and .timestamp < $next_user_ts)] as $turn_responses |
161
+ if ($turn_responses | length) > 0 then {
162
+ id: ("int-" + (($i + 1) | tostring | if length < 3 then "00"[0:(3-length)] + . else . end)),
163
+ timestamp: $user.timestamp,
164
+ user: $user.content,
165
+ thinking: ([$turn_responses[].thinking | select(. != "")] | join("\n")),
166
+ assistant: ([$turn_responses[].text | select(. != "")] | join("\n"))
167
+ } else empty end
168
+ ]
169
+ ' 2>/dev/null || echo '[]')
170
+
171
+ interaction_count=$(echo "$interactions_json" | jq 'length')
172
+
173
+ if [ "$interaction_count" -gt 0 ]; then
174
+ # Escape single quotes for SQL
175
+ interactions_escaped="${interactions_json//\'/\'\'}"
176
+
177
+ # Insert backup into local SQLite with project_path (use master session ID if linked)
178
+ sqlite3 "$db_path" "INSERT INTO pre_compact_backups (session_id, project_path, owner, interactions) VALUES ('${mneme_session_id}', '${project_path_escaped}', '${owner}', '${interactions_escaped}');" 2>/dev/null || true
179
+
180
+ echo "[mneme] PreCompact: Backed up ${interaction_count} interactions to local DB" >&2
181
+ else
182
+ echo "[mneme] PreCompact: No interactions to backup" >&2
183
+ fi
184
+ fi
185
+
186
+ # Continue with compaction (non-blocking)
187
+ echo '{"continue": true}'