@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.
- package/.claude-plugin/plugin.json +29 -0
- package/.mcp.json +18 -0
- package/README.ja.md +400 -0
- package/README.md +410 -0
- package/bin/mneme.js +203 -0
- package/dist/lib/db.js +340 -0
- package/dist/lib/fuzzy-search.js +214 -0
- package/dist/lib/github.js +121 -0
- package/dist/lib/similarity.js +193 -0
- package/dist/lib/utils.js +62 -0
- package/dist/public/apple-touch-icon.png +0 -0
- package/dist/public/assets/index-BgqCALAg.css +1 -0
- package/dist/public/assets/index-EMvn4VEa.js +330 -0
- package/dist/public/assets/react-force-graph-2d-DWoBaKmT.js +46 -0
- package/dist/public/favicon-128-max.png +0 -0
- package/dist/public/favicon-256-max.png +0 -0
- package/dist/public/favicon-32-max.png +0 -0
- package/dist/public/favicon-512-max.png +0 -0
- package/dist/public/favicon-64-max.png +0 -0
- package/dist/public/index.html +15 -0
- package/dist/server.js +4791 -0
- package/dist/servers/db-server.js +30558 -0
- package/dist/servers/search-server.js +30366 -0
- package/hooks/default-tags.json +1055 -0
- package/hooks/hooks.json +61 -0
- package/hooks/post-tool-use.sh +96 -0
- package/hooks/pre-compact.sh +187 -0
- package/hooks/session-end.sh +567 -0
- package/hooks/session-start.sh +380 -0
- package/hooks/user-prompt-submit.sh +253 -0
- package/package.json +77 -0
- package/servers/db-server.ts +993 -0
- package/servers/search-server.ts +675 -0
- package/skills/AGENTS.override.md +5 -0
- package/skills/harvest/skill.md +295 -0
- package/skills/init-mneme/skill.md +101 -0
- package/skills/plan/skill.md +422 -0
- package/skills/report/skill.md +74 -0
- package/skills/resume/skill.md +278 -0
- package/skills/review/skill.md +419 -0
- package/skills/save/skill.md +482 -0
- package/skills/search/skill.md +175 -0
- package/skills/using-mneme/skill.md +185 -0
package/hooks/hooks.json
ADDED
|
@@ -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}'
|