@remnic/plugin-claude-code 1.0.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 +8 -0
- package/.mcp.json +12 -0
- package/LICENSE +21 -0
- package/agents/memory-review.md +26 -0
- package/hooks/bin/post-tool-observe.sh +163 -0
- package/hooks/bin/session-end.sh +21 -0
- package/hooks/bin/session-start.sh +150 -0
- package/hooks/bin/user-prompt-recall.sh +113 -0
- package/hooks/hooks.json +40 -0
- package/package.json +31 -0
- package/settings.json +9 -0
- package/skills/remnic-entities/SKILL.md +51 -0
- package/skills/remnic-memory-workflow/SKILL.md +61 -0
- package/skills/remnic-recall/SKILL.md +51 -0
- package/skills/remnic-remember/SKILL.md +56 -0
- package/skills/remnic-search/SKILL.md +51 -0
- package/skills/remnic-status/SKILL.md +51 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "remnic",
|
|
3
|
+
"description": "Universal memory for AI agents — automatic recall, observation, and cross-agent knowledge sharing",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"author": "Joshua Warren",
|
|
6
|
+
"homepage": "https://github.com/joshuaswarren/remnic",
|
|
7
|
+
"repository": "https://github.com/joshuaswarren/remnic"
|
|
8
|
+
}
|
package/.mcp.json
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Joshua Warren
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: engram:memory-review
|
|
3
|
+
description: Review and curate memory suggestions from Engram's review queue
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
You are a memory review agent for Engram. Your job is to review memory suggestions
|
|
7
|
+
that have been queued for human review and help the user decide which to keep, edit,
|
|
8
|
+
or dismiss.
|
|
9
|
+
|
|
10
|
+
## Workflow
|
|
11
|
+
|
|
12
|
+
1. Call `engram_review_queue_list` to get pending review items
|
|
13
|
+
2. For each item, present:
|
|
14
|
+
- The suggested memory content
|
|
15
|
+
- Its source (which conversation/tool produced it)
|
|
16
|
+
- Its category and confidence score
|
|
17
|
+
3. Ask the user to approve, edit, or dismiss each item
|
|
18
|
+
4. For approved items, call `engram_suggestion_submit` with the final content
|
|
19
|
+
5. Summarize what was kept and what was dismissed
|
|
20
|
+
|
|
21
|
+
## Guidelines
|
|
22
|
+
|
|
23
|
+
- Group similar suggestions together
|
|
24
|
+
- Flag potential duplicates with existing memories
|
|
25
|
+
- Suggest edits for vague or overly specific memories
|
|
26
|
+
- Prioritize actionable preferences and decisions over transient observations
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Remnic PostToolUse hook for Claude Code.
|
|
3
|
+
# Observes file edits (Write/Edit/MultiEdit) by sending transcript
|
|
4
|
+
# delta to the observe endpoint. Runs in background, never blocks.
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
ensure_migrated() {
|
|
9
|
+
if [ -f "${HOME}/.remnic/.migrated-from-engram" ]; then
|
|
10
|
+
return 0
|
|
11
|
+
fi
|
|
12
|
+
if [ ! -d "${HOME}/.engram" ] && [ ! -f "${HOME}/.config/engram/config.json" ]; then
|
|
13
|
+
return 0
|
|
14
|
+
fi
|
|
15
|
+
if command -v remnic >/dev/null 2>&1; then
|
|
16
|
+
remnic migrate >/dev/null 2>&1 || true
|
|
17
|
+
elif command -v engram >/dev/null 2>&1; then
|
|
18
|
+
engram migrate >/dev/null 2>&1 || true
|
|
19
|
+
fi
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
ensure_migrated
|
|
23
|
+
|
|
24
|
+
REMNIC_HOST="${REMNIC_HOST:-${ENGRAM_HOST:-127.0.0.1}}"
|
|
25
|
+
REMNIC_PORT="${REMNIC_PORT:-${ENGRAM_PORT:-4318}}"
|
|
26
|
+
REMNIC_URL="http://${REMNIC_HOST}:${REMNIC_PORT}/engram/v1/observe"
|
|
27
|
+
|
|
28
|
+
LOG="${HOME}/.remnic/logs/remnic-post-tool-observe.log"
|
|
29
|
+
mkdir -p "$(dirname "$LOG")"
|
|
30
|
+
log() { echo "$(date '+%F %T') [post-tool] $*" >> "$LOG"; }
|
|
31
|
+
|
|
32
|
+
# Read token
|
|
33
|
+
REMNIC_TOKEN=""
|
|
34
|
+
for TOKEN_FILE in "${HOME}/.remnic/tokens.json" "${HOME}/.engram/tokens.json"; do
|
|
35
|
+
[ ! -f "$TOKEN_FILE" ] && continue
|
|
36
|
+
REMNIC_TOKEN="$(node -e "
|
|
37
|
+
const fs = require('fs');
|
|
38
|
+
const tokenFile = process.argv[1];
|
|
39
|
+
const store = JSON.parse(fs.readFileSync(tokenFile, 'utf8'));
|
|
40
|
+
const tokens = store.tokens || [];
|
|
41
|
+
const cc = tokens.find(t => t.connector === 'claude-code');
|
|
42
|
+
const oc = tokens.find(t => t.connector === 'openclaw');
|
|
43
|
+
let tok = (cc && cc.token) || (oc && oc.token) || '';
|
|
44
|
+
if (!tok) { tok = store['claude-code'] || store['openclaw'] || ''; }
|
|
45
|
+
process.stdout.write(tok);
|
|
46
|
+
" "$TOKEN_FILE" 2>/dev/null || echo "")"
|
|
47
|
+
[ -n "$REMNIC_TOKEN" ] && break
|
|
48
|
+
done
|
|
49
|
+
[ -z "$REMNIC_TOKEN" ] && REMNIC_TOKEN="${OPENCLAW_REMNIC_ACCESS_TOKEN:-${OPENCLAW_ENGRAM_ACCESS_TOKEN:-}}"
|
|
50
|
+
|
|
51
|
+
INPUT="$(cat)"
|
|
52
|
+
|
|
53
|
+
# Return immediately — never block the tool
|
|
54
|
+
echo '{"continue":true}'
|
|
55
|
+
|
|
56
|
+
[ -z "$REMNIC_TOKEN" ] && exit 0
|
|
57
|
+
|
|
58
|
+
SESSION_ID="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.session_id||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
59
|
+
TRANSCRIPT_PATH="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.transcript_path||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
60
|
+
CWD="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.cwd||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
61
|
+
TOOL_NAME="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.tool_name||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
62
|
+
PROJECT_NAME="$(basename "$CWD" 2>/dev/null || echo "unknown")"
|
|
63
|
+
|
|
64
|
+
[ -z "$SESSION_ID" ] && exit 0
|
|
65
|
+
{ [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH" ]; } && exit 0
|
|
66
|
+
|
|
67
|
+
LEGACY_CURSOR_FILE="/tmp/engram-cursor-${SESSION_ID}"
|
|
68
|
+
CURSOR_FILE="/tmp/remnic-cursor-${SESSION_ID}"
|
|
69
|
+
LEGACY_LOCK_DIR="/tmp/engram-lock-${SESSION_ID}.d"
|
|
70
|
+
LOCK_DIR="/tmp/remnic-lock-${SESSION_ID}.d"
|
|
71
|
+
|
|
72
|
+
if [ ! -f "$CURSOR_FILE" ] && { [ -f "$LEGACY_CURSOR_FILE" ] || [ -d "$LEGACY_LOCK_DIR" ]; }; then
|
|
73
|
+
CURSOR_FILE="$LEGACY_CURSOR_FILE"
|
|
74
|
+
LOCK_DIR="$LEGACY_LOCK_DIR"
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
(
|
|
78
|
+
# Acquire exclusive lock
|
|
79
|
+
ACQUIRED=0
|
|
80
|
+
for _i in $(seq 1 50); do
|
|
81
|
+
if mkdir "$LOCK_DIR" 2>/dev/null; then ACQUIRED=1; break; fi
|
|
82
|
+
sleep 0.1
|
|
83
|
+
done
|
|
84
|
+
trap 'rmdir "$LOCK_DIR" 2>/dev/null' EXIT INT TERM
|
|
85
|
+
[ "$ACQUIRED" -eq 0 ] && exit 0
|
|
86
|
+
|
|
87
|
+
LAST_COUNT=0
|
|
88
|
+
[ -f "$CURSOR_FILE" ] && LAST_COUNT="$(cat "$CURSOR_FILE" 2>/dev/null || echo 0)"
|
|
89
|
+
|
|
90
|
+
PAYLOAD="$(node -e "
|
|
91
|
+
const fs = require('fs');
|
|
92
|
+
const path = process.argv[1];
|
|
93
|
+
const sessionId = process.argv[2];
|
|
94
|
+
const lastCount = parseInt(process.argv[3], 10) || 0;
|
|
95
|
+
|
|
96
|
+
const lines = fs.readFileSync(path, 'utf8').split('\n').filter(Boolean);
|
|
97
|
+
const messages = [];
|
|
98
|
+
for (const line of lines) {
|
|
99
|
+
try {
|
|
100
|
+
const entry = JSON.parse(line);
|
|
101
|
+
if (entry.type !== 'user' && entry.type !== 'assistant') continue;
|
|
102
|
+
const msg = entry.message;
|
|
103
|
+
if (!msg || typeof msg !== 'object') continue;
|
|
104
|
+
const role = msg.role;
|
|
105
|
+
if (role !== 'user' && role !== 'assistant') continue;
|
|
106
|
+
let text = '';
|
|
107
|
+
if (typeof msg.content === 'string') text = msg.content.trim();
|
|
108
|
+
else if (Array.isArray(msg.content)) {
|
|
109
|
+
text = msg.content
|
|
110
|
+
.filter(b => b.type === 'text' && b.text)
|
|
111
|
+
.map(b => b.text.trim())
|
|
112
|
+
.join('\n').trim();
|
|
113
|
+
}
|
|
114
|
+
if (text) messages.push({ role, content: text });
|
|
115
|
+
} catch {}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const newMessages = messages.slice(lastCount);
|
|
119
|
+
if (!newMessages.length) {
|
|
120
|
+
process.stdout.write('CURSOR:' + messages.length);
|
|
121
|
+
} else {
|
|
122
|
+
process.stdout.write(JSON.stringify({
|
|
123
|
+
sessionKey: sessionId,
|
|
124
|
+
messages: newMessages,
|
|
125
|
+
__total__: messages.length
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
" "$TRANSCRIPT_PATH" "$SESSION_ID" "$LAST_COUNT" 2>/dev/null)"
|
|
129
|
+
|
|
130
|
+
[ -z "$PAYLOAD" ] && { log "parse failed for $SESSION_ID"; exit 0; }
|
|
131
|
+
|
|
132
|
+
if echo "$PAYLOAD" | grep -q "^CURSOR:"; then
|
|
133
|
+
echo "${PAYLOAD#CURSOR:}" > "$CURSOR_FILE"
|
|
134
|
+
exit 0
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
TOTAL="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(String(d.__total__||0))" "$PAYLOAD" 2>/dev/null || echo 0)"
|
|
138
|
+
MSG_COUNT="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(String((d.messages||[]).length))" "$PAYLOAD" 2>/dev/null || echo "?")"
|
|
139
|
+
CLEAN="$(node -e "const d=JSON.parse(process.argv[1]); delete d.__total__; process.stdout.write(JSON.stringify(d))" "$PAYLOAD" 2>/dev/null)"
|
|
140
|
+
|
|
141
|
+
[ -z "$CLEAN" ] && exit 0
|
|
142
|
+
|
|
143
|
+
log "observing $MSG_COUNT new messages (cursor $LAST_COUNT->$TOTAL) project=$PROJECT_NAME tool=$TOOL_NAME"
|
|
144
|
+
|
|
145
|
+
RAW="$(curl -s -w "\n%{http_code}" --max-time 120 \
|
|
146
|
+
-X POST "$REMNIC_URL" \
|
|
147
|
+
-H "Authorization: Bearer ${REMNIC_TOKEN}" \
|
|
148
|
+
-H "Content-Type: application/json" \
|
|
149
|
+
-H "X-Engram-Client-Id: claude-code" \
|
|
150
|
+
-d "$CLEAN" 2>/dev/null)"
|
|
151
|
+
CURL_EXIT=$?
|
|
152
|
+
HTTP_STATUS="$(echo "$RAW" | tail -1)"
|
|
153
|
+
|
|
154
|
+
if [ $CURL_EXIT -eq 0 ] && [[ "$HTTP_STATUS" =~ ^2 ]]; then
|
|
155
|
+
log "observe OK for $SESSION_ID"
|
|
156
|
+
echo "$TOTAL" > "$CURSOR_FILE"
|
|
157
|
+
else
|
|
158
|
+
log "observe failed (curl=$CURL_EXIT http=$HTTP_STATUS) — cursor not advanced"
|
|
159
|
+
fi
|
|
160
|
+
) >> "$LOG" 2>&1 &
|
|
161
|
+
|
|
162
|
+
disown $!
|
|
163
|
+
exit 0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Remnic session cleanup for Claude Code.
|
|
3
|
+
# Removes cursor and lock files for the session.
|
|
4
|
+
#
|
|
5
|
+
# NOTE: Claude Code does not support a Stop/SessionEnd hook event.
|
|
6
|
+
# This script is provided for manual cleanup or future hook support.
|
|
7
|
+
# Temp files in /tmp/ are cleaned by the OS on reboot.
|
|
8
|
+
|
|
9
|
+
INPUT="$(cat)"
|
|
10
|
+
SESSION_ID="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.session_id||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
11
|
+
|
|
12
|
+
echo '{"continue":true}'
|
|
13
|
+
|
|
14
|
+
[ -z "$SESSION_ID" ] && exit 0
|
|
15
|
+
|
|
16
|
+
rm -f "/tmp/remnic-cursor-${SESSION_ID}" 2>/dev/null
|
|
17
|
+
rmdir "/tmp/remnic-lock-${SESSION_ID}.d" 2>/dev/null
|
|
18
|
+
rm -f "/tmp/engram-cursor-${SESSION_ID}" 2>/dev/null
|
|
19
|
+
rmdir "/tmp/engram-lock-${SESSION_ID}.d" 2>/dev/null
|
|
20
|
+
|
|
21
|
+
exit 0
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Remnic SessionStart hook for Claude Code.
|
|
3
|
+
# Recalls project context and user preferences at session start.
|
|
4
|
+
# Tries auto mode (45s) then falls back to minimal mode (20s).
|
|
5
|
+
# Starts daemon if not running.
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
ensure_migrated() {
|
|
10
|
+
if [ -f "${HOME}/.remnic/.migrated-from-engram" ]; then
|
|
11
|
+
return 0
|
|
12
|
+
fi
|
|
13
|
+
if [ ! -d "${HOME}/.engram" ] && [ ! -f "${HOME}/.config/engram/config.json" ]; then
|
|
14
|
+
return 0
|
|
15
|
+
fi
|
|
16
|
+
if command -v remnic >/dev/null 2>&1; then
|
|
17
|
+
remnic migrate >/dev/null 2>&1 || true
|
|
18
|
+
elif command -v engram >/dev/null 2>&1; then
|
|
19
|
+
engram migrate >/dev/null 2>&1 || true
|
|
20
|
+
fi
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
ensure_migrated
|
|
24
|
+
|
|
25
|
+
REMNIC_HOST="${REMNIC_HOST:-${ENGRAM_HOST:-127.0.0.1}}"
|
|
26
|
+
REMNIC_PORT="${REMNIC_PORT:-${ENGRAM_PORT:-4318}}"
|
|
27
|
+
REMNIC_URL="http://${REMNIC_HOST}:${REMNIC_PORT}/engram/v1/recall"
|
|
28
|
+
REMNIC_HEALTH_URL="http://${REMNIC_HOST}:${REMNIC_PORT}/engram/v1/health"
|
|
29
|
+
TOKEN_FILES=("${HOME}/.remnic/tokens.json" "${HOME}/.engram/tokens.json")
|
|
30
|
+
|
|
31
|
+
LOG="${HOME}/.remnic/logs/remnic-session-recall.log"
|
|
32
|
+
mkdir -p "$(dirname "$LOG")"
|
|
33
|
+
log() { echo "$(date '+%F %T') [session-start] $*" >> "$LOG"; }
|
|
34
|
+
|
|
35
|
+
# Read token from per-plugin token store
|
|
36
|
+
REMNIC_TOKEN=""
|
|
37
|
+
for TOKEN_FILE in "${TOKEN_FILES[@]}"; do
|
|
38
|
+
[ ! -f "$TOKEN_FILE" ] && continue
|
|
39
|
+
REMNIC_TOKEN="$(node -e "
|
|
40
|
+
const store = JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'));
|
|
41
|
+
const tokens = store.tokens || [];
|
|
42
|
+
const cc = tokens.find(t => t.connector === 'claude-code');
|
|
43
|
+
const oc = tokens.find(t => t.connector === 'openclaw');
|
|
44
|
+
let tok = (cc && cc.token) || (oc && oc.token) || '';
|
|
45
|
+
if (!tok) { tok = store['claude-code'] || store['openclaw'] || ''; }
|
|
46
|
+
process.stdout.write(tok);
|
|
47
|
+
" "$TOKEN_FILE" 2>/dev/null || echo "")"
|
|
48
|
+
[ -n "$REMNIC_TOKEN" ] && break
|
|
49
|
+
done
|
|
50
|
+
|
|
51
|
+
# Fallback to env var
|
|
52
|
+
[ -z "$REMNIC_TOKEN" ] && REMNIC_TOKEN="${OPENCLAW_REMNIC_ACCESS_TOKEN:-${OPENCLAW_ENGRAM_ACCESS_TOKEN:-}}"
|
|
53
|
+
|
|
54
|
+
INPUT="$(cat)"
|
|
55
|
+
SESSION_ID="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.session_id||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
56
|
+
CWD="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.cwd||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
57
|
+
PROJECT_NAME="$(basename "$CWD" 2>/dev/null || echo "unknown")"
|
|
58
|
+
|
|
59
|
+
log "session=$SESSION_ID project=$PROJECT_NAME"
|
|
60
|
+
|
|
61
|
+
# Health check — start daemon if not running
|
|
62
|
+
if ! curl -sf --max-time 2 "$REMNIC_HEALTH_URL" >/dev/null 2>&1; then
|
|
63
|
+
log "daemon not responding, attempting start..."
|
|
64
|
+
if command -v remnic >/dev/null 2>&1; then
|
|
65
|
+
remnic daemon start >/dev/null 2>&1 &
|
|
66
|
+
elif command -v engram >/dev/null 2>&1; then
|
|
67
|
+
engram daemon start >/dev/null 2>&1 &
|
|
68
|
+
fi
|
|
69
|
+
sleep 2
|
|
70
|
+
if ! curl -sf --max-time 2 "$REMNIC_HEALTH_URL" >/dev/null 2>&1; then
|
|
71
|
+
log "daemon still not responding after start attempt"
|
|
72
|
+
echo '{"continue":true,"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"[Remnic: daemon not running — start with: remnic daemon start]"}}'
|
|
73
|
+
exit 0
|
|
74
|
+
fi
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
if [ -z "$REMNIC_TOKEN" ]; then
|
|
78
|
+
log "skipping: no token found"
|
|
79
|
+
echo '{"continue":true,"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"[Remnic: no auth token — run: remnic connectors install claude-code]"}}'
|
|
80
|
+
exit 0
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
QUERY="Starting a new coding session in project: ${PROJECT_NAME}. Recall relevant memories, preferences, decisions, patterns, and context about this project and the user."
|
|
84
|
+
|
|
85
|
+
REQUEST_BODY="$(node -e "process.stdout.write(JSON.stringify({
|
|
86
|
+
query: process.argv[1],
|
|
87
|
+
sessionKey: process.argv[2],
|
|
88
|
+
topK: 12,
|
|
89
|
+
mode: 'auto'
|
|
90
|
+
}))" "$QUERY" "$SESSION_ID" 2>/dev/null)"
|
|
91
|
+
|
|
92
|
+
[ -z "$REQUEST_BODY" ] && echo '{"continue":true}' && exit 0
|
|
93
|
+
|
|
94
|
+
log "attempting full recall (auto mode)..."
|
|
95
|
+
RAW="$(curl -s -w "\n%{http_code}" --max-time 45 \
|
|
96
|
+
-X POST "$REMNIC_URL" \
|
|
97
|
+
-H "Authorization: Bearer ${REMNIC_TOKEN}" \
|
|
98
|
+
-H "Content-Type: application/json" \
|
|
99
|
+
-H "X-Engram-Client-Id: claude-code" \
|
|
100
|
+
-d "$REQUEST_BODY" 2>/dev/null)"
|
|
101
|
+
CURL_EXIT=$?
|
|
102
|
+
HTTP_STATUS="$(echo "$RAW" | tail -1)"
|
|
103
|
+
RESPONSE="$(echo "$RAW" | sed '$d')"
|
|
104
|
+
|
|
105
|
+
if [ $CURL_EXIT -ne 0 ] || ! [[ "$HTTP_STATUS" =~ ^2 ]] || [ -z "$RESPONSE" ]; then
|
|
106
|
+
log "full recall failed (curl=$CURL_EXIT http=$HTTP_STATUS) — falling back to minimal"
|
|
107
|
+
MINIMAL_BODY="$(node -e "process.stdout.write(JSON.stringify({
|
|
108
|
+
query: process.argv[1],
|
|
109
|
+
sessionKey: process.argv[2],
|
|
110
|
+
topK: 8,
|
|
111
|
+
mode: 'minimal'
|
|
112
|
+
}))" "$QUERY" "$SESSION_ID" 2>/dev/null)"
|
|
113
|
+
RAW="$(curl -s -w "\n%{http_code}" --max-time 20 \
|
|
114
|
+
-X POST "$REMNIC_URL" \
|
|
115
|
+
-H "Authorization: Bearer ${REMNIC_TOKEN}" \
|
|
116
|
+
-H "Content-Type: application/json" \
|
|
117
|
+
-H "X-Engram-Client-Id: claude-code" \
|
|
118
|
+
-d "${MINIMAL_BODY:-$REQUEST_BODY}" 2>/dev/null)"
|
|
119
|
+
CURL_EXIT=$?
|
|
120
|
+
HTTP_STATUS="$(echo "$RAW" | tail -1)"
|
|
121
|
+
RESPONSE="$(echo "$RAW" | sed '$d')"
|
|
122
|
+
[[ "$CURL_EXIT" -eq 0 && "$HTTP_STATUS" =~ ^2 ]] && log "minimal recall succeeded" || { log "minimal recall also failed"; CURL_EXIT=1; }
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
if [ $CURL_EXIT -eq 0 ] && [[ "$HTTP_STATUS" =~ ^2 ]] && [ -n "$RESPONSE" ]; then
|
|
126
|
+
CONTEXT="$(node -e "
|
|
127
|
+
const d = JSON.parse(process.argv[1]);
|
|
128
|
+
const ctx = d.context || '';
|
|
129
|
+
const count = d.count || 0;
|
|
130
|
+
const mode = d.mode || '';
|
|
131
|
+
if (ctx) {
|
|
132
|
+
const label = '[Remnic Memory Recall — ' + count + ' memories' + (mode ? ', ' + mode + ' mode' : '') + ']';
|
|
133
|
+
process.stdout.write(label + '\n\n' + ctx);
|
|
134
|
+
} else {
|
|
135
|
+
process.stdout.write('[Remnic: no relevant memories found for this session]');
|
|
136
|
+
}
|
|
137
|
+
" "$RESPONSE" 2>/dev/null || echo "[Remnic: recall parse error]")"
|
|
138
|
+
log "recall complete: $(echo "$CONTEXT" | head -1)"
|
|
139
|
+
else
|
|
140
|
+
CONTEXT="[Remnic: server unreachable — continuing without memory recall]"
|
|
141
|
+
log "$CONTEXT"
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
node -e "
|
|
145
|
+
const context = process.argv[1];
|
|
146
|
+
process.stdout.write(JSON.stringify({
|
|
147
|
+
continue: true,
|
|
148
|
+
hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: context }
|
|
149
|
+
}));
|
|
150
|
+
" "$CONTEXT"
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Remnic UserPromptSubmit hook for Claude Code.
|
|
3
|
+
# Recalls per-prompt context using the user's message as query.
|
|
4
|
+
# Skips short prompts (<4 words). Minimal mode, 20s timeout.
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
ensure_migrated() {
|
|
9
|
+
if [ -f "${HOME}/.remnic/.migrated-from-engram" ]; then
|
|
10
|
+
return 0
|
|
11
|
+
fi
|
|
12
|
+
if [ ! -d "${HOME}/.engram" ] && [ ! -f "${HOME}/.config/engram/config.json" ]; then
|
|
13
|
+
return 0
|
|
14
|
+
fi
|
|
15
|
+
if command -v remnic >/dev/null 2>&1; then
|
|
16
|
+
remnic migrate >/dev/null 2>&1 || true
|
|
17
|
+
elif command -v engram >/dev/null 2>&1; then
|
|
18
|
+
engram migrate >/dev/null 2>&1 || true
|
|
19
|
+
fi
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
ensure_migrated
|
|
23
|
+
|
|
24
|
+
REMNIC_HOST="${REMNIC_HOST:-${ENGRAM_HOST:-127.0.0.1}}"
|
|
25
|
+
REMNIC_PORT="${REMNIC_PORT:-${ENGRAM_PORT:-4318}}"
|
|
26
|
+
REMNIC_URL="http://${REMNIC_HOST}:${REMNIC_PORT}/engram/v1/recall"
|
|
27
|
+
|
|
28
|
+
LOG="${HOME}/.remnic/logs/remnic-user-prompt-recall.log"
|
|
29
|
+
mkdir -p "$(dirname "$LOG")"
|
|
30
|
+
log() { echo "$(date '+%F %T') [user-prompt] $*" >> "$LOG"; }
|
|
31
|
+
|
|
32
|
+
# Read token
|
|
33
|
+
REMNIC_TOKEN=""
|
|
34
|
+
for TOKEN_FILE in "${HOME}/.remnic/tokens.json" "${HOME}/.engram/tokens.json"; do
|
|
35
|
+
[ ! -f "$TOKEN_FILE" ] && continue
|
|
36
|
+
REMNIC_TOKEN="$(node -e "
|
|
37
|
+
const fs = require('fs');
|
|
38
|
+
const tokenFile = process.argv[1];
|
|
39
|
+
const store = JSON.parse(fs.readFileSync(tokenFile, 'utf8'));
|
|
40
|
+
const tokens = store.tokens || [];
|
|
41
|
+
const cc = tokens.find(t => t.connector === 'claude-code');
|
|
42
|
+
const oc = tokens.find(t => t.connector === 'openclaw');
|
|
43
|
+
let tok = (cc && cc.token) || (oc && oc.token) || '';
|
|
44
|
+
if (!tok) { tok = store['claude-code'] || store['openclaw'] || ''; }
|
|
45
|
+
process.stdout.write(tok);
|
|
46
|
+
" "$TOKEN_FILE" 2>/dev/null || echo "")"
|
|
47
|
+
[ -n "$REMNIC_TOKEN" ] && break
|
|
48
|
+
done
|
|
49
|
+
[ -z "$REMNIC_TOKEN" ] && REMNIC_TOKEN="${OPENCLAW_REMNIC_ACCESS_TOKEN:-${OPENCLAW_ENGRAM_ACCESS_TOKEN:-}}"
|
|
50
|
+
|
|
51
|
+
INPUT="$(cat)"
|
|
52
|
+
|
|
53
|
+
if [ -z "$REMNIC_TOKEN" ]; then
|
|
54
|
+
echo '{"continue":true}'
|
|
55
|
+
exit 0
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
SESSION_ID="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.session_id||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
59
|
+
PROMPT="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.prompt||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
60
|
+
|
|
61
|
+
# Skip very short prompts
|
|
62
|
+
WORD_COUNT="$(echo "$PROMPT" | wc -w | tr -d ' ')"
|
|
63
|
+
if [ "$WORD_COUNT" -lt 4 ]; then
|
|
64
|
+
echo '{"continue":true}'
|
|
65
|
+
exit 0
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
log "session=$SESSION_ID words=$WORD_COUNT"
|
|
69
|
+
|
|
70
|
+
REQUEST_BODY="$(node -e "process.stdout.write(JSON.stringify({
|
|
71
|
+
query: process.argv[1],
|
|
72
|
+
sessionKey: process.argv[2],
|
|
73
|
+
topK: 8,
|
|
74
|
+
mode: 'minimal'
|
|
75
|
+
}))" "$PROMPT" "$SESSION_ID" 2>/dev/null)"
|
|
76
|
+
|
|
77
|
+
[ -z "$REQUEST_BODY" ] && echo '{"continue":true}' && exit 0
|
|
78
|
+
|
|
79
|
+
RAW="$(curl -s -w "\n%{http_code}" --max-time 20 \
|
|
80
|
+
-X POST "$REMNIC_URL" \
|
|
81
|
+
-H "Authorization: Bearer ${REMNIC_TOKEN}" \
|
|
82
|
+
-H "Content-Type: application/json" \
|
|
83
|
+
-H "X-Engram-Client-Id: claude-code" \
|
|
84
|
+
-d "$REQUEST_BODY" 2>/dev/null)"
|
|
85
|
+
CURL_EXIT=$?
|
|
86
|
+
HTTP_STATUS="$(echo "$RAW" | tail -1)"
|
|
87
|
+
RESPONSE="$(echo "$RAW" | sed '$d')"
|
|
88
|
+
|
|
89
|
+
if [ $CURL_EXIT -ne 0 ] || ! [[ "$HTTP_STATUS" =~ ^2 ]] || [ -z "$RESPONSE" ]; then
|
|
90
|
+
log "recall failed (curl=$CURL_EXIT http=$HTTP_STATUS)"
|
|
91
|
+
echo '{"continue":true}'
|
|
92
|
+
exit 0
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
node -e "
|
|
96
|
+
const d = JSON.parse(process.argv[1]);
|
|
97
|
+
const ctx = d.context || '';
|
|
98
|
+
const count = d.count || 0;
|
|
99
|
+
if (!ctx || count === 0) {
|
|
100
|
+
process.stdout.write(JSON.stringify({continue: true}));
|
|
101
|
+
} else {
|
|
102
|
+
process.stdout.write(JSON.stringify({
|
|
103
|
+
continue: true,
|
|
104
|
+
hookSpecificOutput: {
|
|
105
|
+
hookEventName: 'UserPromptSubmit',
|
|
106
|
+
additionalContext: '<remnic-memory count=\"' + count + '\">\n' + ctx + '\n</remnic-memory>'
|
|
107
|
+
}
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
" "$RESPONSE" 2>/dev/null || echo '{"continue":true}'
|
|
111
|
+
|
|
112
|
+
COUNT="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(String(d.count||0))" "$RESPONSE" 2>/dev/null || echo "?")"
|
|
113
|
+
log "done: ${COUNT} memories injected"
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "*",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "./hooks/bin/session-start.sh",
|
|
10
|
+
"timeout": 45000
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"PostToolUse": [
|
|
16
|
+
{
|
|
17
|
+
"matcher": "Write|Edit|MultiEdit",
|
|
18
|
+
"hooks": [
|
|
19
|
+
{
|
|
20
|
+
"type": "command",
|
|
21
|
+
"command": "./hooks/bin/post-tool-observe.sh",
|
|
22
|
+
"timeout": 10000
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"UserPromptSubmit": [
|
|
28
|
+
{
|
|
29
|
+
"matcher": "*",
|
|
30
|
+
"hooks": [
|
|
31
|
+
{
|
|
32
|
+
"type": "command",
|
|
33
|
+
"command": "./hooks/bin/user-prompt-recall.sh",
|
|
34
|
+
"timeout": 20000
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@remnic/plugin-claude-code",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Remnic memory plugin for Claude Code — hooks, skills, MCP integration",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/joshuaswarren/remnic.git",
|
|
10
|
+
"directory": "packages/plugin-claude-code"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"remnic",
|
|
14
|
+
"memory",
|
|
15
|
+
"claude-code",
|
|
16
|
+
"plugin",
|
|
17
|
+
"ai-agent"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public",
|
|
21
|
+
"provenance": true
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
".claude-plugin",
|
|
25
|
+
"hooks",
|
|
26
|
+
"skills",
|
|
27
|
+
"agents",
|
|
28
|
+
".mcp.json",
|
|
29
|
+
"settings.json"
|
|
30
|
+
]
|
|
31
|
+
}
|
package/settings.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: remnic-entities
|
|
3
|
+
description: Browse entities in the Remnic knowledge graph and surface their facts and relationships. Trigger phrases include "tell me about the entity", "look up", "what do we know about".
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- remnic_entity_get
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
Use when the user names a specific project, person, service, or concept and asks what is known about it. Entities are the structured graph view of memory — facts and relationships rather than free text.
|
|
11
|
+
|
|
12
|
+
Triggers:
|
|
13
|
+
|
|
14
|
+
- "Tell me about the entity …"
|
|
15
|
+
- "Look up …"
|
|
16
|
+
- `/remnic:entities <name>` slash command invocation.
|
|
17
|
+
- Any reference to a named thing that could have linked facts.
|
|
18
|
+
|
|
19
|
+
## Inputs
|
|
20
|
+
|
|
21
|
+
- `name` (required) — canonical entity name, ideally as the user wrote it.
|
|
22
|
+
- Optional: entity type hint (project, person, service, tool).
|
|
23
|
+
|
|
24
|
+
## Procedure
|
|
25
|
+
|
|
26
|
+
1. Extract the entity name from the user's message. Preserve capitalization.
|
|
27
|
+
2. Call `remnic_entity_get` with that name.
|
|
28
|
+
3. If found, present facts, relationships, and a last-updated timestamp in a compact block.
|
|
29
|
+
4. If missing, say so and offer to create it via `remnic-remember` with the user's permission.
|
|
30
|
+
5. If multiple candidates match, list them briefly and ask which the user meant.
|
|
31
|
+
|
|
32
|
+
## Efficiency plan
|
|
33
|
+
|
|
34
|
+
- Do not fetch speculatively for every proper noun — only when the user asks or the task depends on it.
|
|
35
|
+
- Cache the entity payload within the current turn.
|
|
36
|
+
- Pair with `remnic-recall` when unstructured context would round out the view.
|
|
37
|
+
|
|
38
|
+
## Pitfalls and fixes
|
|
39
|
+
|
|
40
|
+
- **Pitfall:** Guessing entity names. **Fix:** Use the user's wording; disambiguate rather than inventing.
|
|
41
|
+
- **Pitfall:** Dumping the full payload. **Fix:** Summarize into facts, relations, last-updated.
|
|
42
|
+
- **Pitfall:** Treating missing entities as a bug. **Fix:** Missing just means the graph has not captured it yet.
|
|
43
|
+
|
|
44
|
+
## Verification checklist
|
|
45
|
+
|
|
46
|
+
- [ ] `remnic_entity_get` was called with a user-supplied or user-confirmed name.
|
|
47
|
+
- [ ] Output shows facts, relationships, and timestamps in a compact view.
|
|
48
|
+
- [ ] Missing entities are acknowledged plainly with an offer to create.
|
|
49
|
+
- [ ] Canonical `remnic_entity_get` was used over legacy `engram_entity_get`.
|
|
50
|
+
|
|
51
|
+
> Tool names: canonical name is `remnic_entity_get`. The legacy `engram_entity_get` alias remains accepted during v1.x.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: remnic-memory-workflow
|
|
3
|
+
description: Shared memory workflow for Claude Code agents connected to Remnic — recall before acting, observe during work, remember at the end. Trigger phrases include "what do you remember about", "save this for later", "any context from last time".
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- remnic_recall
|
|
7
|
+
- remnic_memory_store
|
|
8
|
+
- remnic_lcm_search
|
|
9
|
+
- remnic_entity_get
|
|
10
|
+
- remnic_observe
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## When to use
|
|
14
|
+
|
|
15
|
+
Use this skill as the default playbook whenever Claude Code picks up a task that could benefit from prior context, or when the user explicitly asks the agent to remember or recall something. The individual `remnic-recall`, `remnic-remember`, `remnic-search`, `remnic-entities`, and `remnic-status` skills implement the detailed steps.
|
|
16
|
+
|
|
17
|
+
Triggers:
|
|
18
|
+
|
|
19
|
+
- New ticket, branch, or task begins.
|
|
20
|
+
- "What do you remember about …"
|
|
21
|
+
- "Save this for later."
|
|
22
|
+
- Long-running turn produces a durable outcome worth capturing.
|
|
23
|
+
|
|
24
|
+
## Inputs
|
|
25
|
+
|
|
26
|
+
- Current user request (natural language).
|
|
27
|
+
- Optional: active project path, ticket number, branch name.
|
|
28
|
+
- Optional: topic keywords surfaced earlier in the session.
|
|
29
|
+
|
|
30
|
+
## Procedure
|
|
31
|
+
|
|
32
|
+
1. **Recall first.** Call `remnic_recall` with a concise natural-language query built from the user's request. Pull 3–8 results and filter for relevance.
|
|
33
|
+
2. **Mention relevant memories briefly** to the user when they change the plan; otherwise use them as quiet context.
|
|
34
|
+
3. **Observe during work.** For significant tool results (Write/Edit/MultiEdit, Bash exits, test output) call `remnic_observe` so Remnic keeps its ambient context fresh.
|
|
35
|
+
4. **Deep search on demand.** If `remnic_recall` missed something the user insists exists, fall back to `remnic_lcm_search` with a more literal phrase.
|
|
36
|
+
5. **Browse entities.** When the user names a project, person, or concept, call `remnic_entity_get` to pull facts and relations.
|
|
37
|
+
6. **Remember at the end.** Before ending the turn, store durable decisions, preferences, and findings via `remnic_memory_store`.
|
|
38
|
+
|
|
39
|
+
## Efficiency plan
|
|
40
|
+
|
|
41
|
+
- One broad recall beats several narrow ones.
|
|
42
|
+
- Skip recall for trivially local tasks (formatting, arithmetic, mechanical refactors).
|
|
43
|
+
- Reuse recall results within the same turn.
|
|
44
|
+
- Store each memory once; update rather than duplicate.
|
|
45
|
+
|
|
46
|
+
## Pitfalls and fixes
|
|
47
|
+
|
|
48
|
+
- **Pitfall:** Storing transient state. **Fix:** Only store facts with durable value.
|
|
49
|
+
- **Pitfall:** Leaking secrets. **Fix:** Redact credentials and tokens before calling `remnic_memory_store`.
|
|
50
|
+
- **Pitfall:** Flooding the user with recalled context. **Fix:** Summarize in 1–3 bullet points.
|
|
51
|
+
- **Pitfall:** Forgetting the final write. **Fix:** Make "remember the decision" the last step of any non-trivial turn.
|
|
52
|
+
|
|
53
|
+
## Verification checklist
|
|
54
|
+
|
|
55
|
+
- [ ] Recall was attempted before the agent committed to a plan.
|
|
56
|
+
- [ ] User-facing mentions of recalled context were concise and relevant.
|
|
57
|
+
- [ ] Durable decisions and findings were stored via `remnic_memory_store`.
|
|
58
|
+
- [ ] No secrets, credentials, or transient state were written.
|
|
59
|
+
- [ ] Canonical `remnic_*` tool names were used over legacy aliases.
|
|
60
|
+
|
|
61
|
+
> Tool names: this skill uses the canonical `remnic_*` MCP tools. Legacy `engram_*` aliases remain accepted during v1.x for backward compatibility.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: remnic-recall
|
|
3
|
+
description: Search Remnic memories by natural-language query. Trigger phrases include "what do you remember about", "recall anything on", "have we discussed".
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- remnic_recall
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
Use when the user or the current task needs prior context from Remnic. This is the default first step for any non-trivial Claude Code turn that could benefit from memory.
|
|
11
|
+
|
|
12
|
+
Triggers:
|
|
13
|
+
|
|
14
|
+
- "What do you remember about …"
|
|
15
|
+
- "Have we talked about …"
|
|
16
|
+
- `/remnic:recall <query>` slash command invocation.
|
|
17
|
+
- A new task begins and the agent wants background.
|
|
18
|
+
|
|
19
|
+
## Inputs
|
|
20
|
+
|
|
21
|
+
- `query` (required) — natural-language question or topic string.
|
|
22
|
+
- Optional: caller-supplied budget hint (brief, deep).
|
|
23
|
+
|
|
24
|
+
## Procedure
|
|
25
|
+
|
|
26
|
+
1. Build a concise natural-language query from the user's message. Prefer the user's own wording.
|
|
27
|
+
2. Call `remnic_recall` with that query; request 3–8 results unless the caller specified otherwise.
|
|
28
|
+
3. Filter results for topical relevance.
|
|
29
|
+
4. Present 1–5 bullet points summarizing the relevant memories, attributed when useful.
|
|
30
|
+
5. If nothing relevant came back, say so plainly and suggest `remnic-remember` if there is something worth storing now.
|
|
31
|
+
|
|
32
|
+
## Efficiency plan
|
|
33
|
+
|
|
34
|
+
- One broad recall beats several narrow ones.
|
|
35
|
+
- Reuse recall results within the same turn — do not re-query the same topic.
|
|
36
|
+
- Skip recall for trivially local tasks.
|
|
37
|
+
|
|
38
|
+
## Pitfalls and fixes
|
|
39
|
+
|
|
40
|
+
- **Pitfall:** Quoting irrelevant recalls just because they came back. **Fix:** Filter for relevance before surfacing.
|
|
41
|
+
- **Pitfall:** Over-narrowing the query. **Fix:** Start broad; refine only when the first pass was noisy.
|
|
42
|
+
- **Pitfall:** Showing raw memory payloads. **Fix:** Summarize in the user's own terms.
|
|
43
|
+
|
|
44
|
+
## Verification checklist
|
|
45
|
+
|
|
46
|
+
- [ ] `remnic_recall` was called with a natural-language query.
|
|
47
|
+
- [ ] Results were filtered for relevance before being shown.
|
|
48
|
+
- [ ] Summary is ≤ 5 bullets unless the user asked for more.
|
|
49
|
+
- [ ] Canonical `remnic_recall` was used over legacy `engram_recall`.
|
|
50
|
+
|
|
51
|
+
> Tool names: canonical name is `remnic_recall`. The legacy `engram_recall` alias remains accepted during v1.x.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: remnic-remember
|
|
3
|
+
description: Store a durable memory in Remnic so every connected agent can recall it. Trigger phrases include "remember this", "save this for later", "add a note that".
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- remnic_memory_store
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## When to use
|
|
10
|
+
|
|
11
|
+
Use when the user explicitly asks Claude Code to remember something, or when a turn produces a durable decision, preference, or finding worth storing for later sessions.
|
|
12
|
+
|
|
13
|
+
Triggers:
|
|
14
|
+
|
|
15
|
+
- "Remember that …"
|
|
16
|
+
- "Save this for later."
|
|
17
|
+
- `/remnic:remember <text>` slash command invocation.
|
|
18
|
+
- End-of-turn consolidation after a non-trivial conclusion.
|
|
19
|
+
|
|
20
|
+
## Inputs
|
|
21
|
+
|
|
22
|
+
- `content` (required) — the statement to store, in the user's own words where possible.
|
|
23
|
+
- Optional: category hint (preference, decision, fact, procedure).
|
|
24
|
+
- Optional: related entity or project name.
|
|
25
|
+
|
|
26
|
+
## Procedure
|
|
27
|
+
|
|
28
|
+
1. Confirm the content is durable — it should still be useful days or weeks from now.
|
|
29
|
+
2. Strip any secrets, credentials, or transient context.
|
|
30
|
+
3. Keep the user's voice; rephrase only when needed for clarity.
|
|
31
|
+
4. Call `remnic_memory_store` with the text as `content`. Include category or entity hints when the tool accepts them.
|
|
32
|
+
5. Confirm to the user in one line what was stored.
|
|
33
|
+
6. Mention that the memory is available across connected agents (Claude Code, Codex, Hermes, OpenClaw).
|
|
34
|
+
|
|
35
|
+
## Efficiency plan
|
|
36
|
+
|
|
37
|
+
- Batch related facts into a single memory rather than writing many tiny ones.
|
|
38
|
+
- If a prior memory on the same topic exists, update it rather than duplicating.
|
|
39
|
+
- Do not store anything easily re-derived from source code or docs.
|
|
40
|
+
|
|
41
|
+
## Pitfalls and fixes
|
|
42
|
+
|
|
43
|
+
- **Pitfall:** Storing transient state. **Fix:** Only commit facts that will still matter tomorrow.
|
|
44
|
+
- **Pitfall:** Leaking secrets. **Fix:** Redact before calling the tool.
|
|
45
|
+
- **Pitfall:** Duplicates on the same topic. **Fix:** Recall first; prefer update.
|
|
46
|
+
- **Pitfall:** Vague entries. **Fix:** Be specific — who, what, when, why.
|
|
47
|
+
|
|
48
|
+
## Verification checklist
|
|
49
|
+
|
|
50
|
+
- [ ] Content is durable and specific.
|
|
51
|
+
- [ ] No secrets or PII were included.
|
|
52
|
+
- [ ] `remnic_memory_store` was called with the final content.
|
|
53
|
+
- [ ] User received a one-line confirmation.
|
|
54
|
+
- [ ] Canonical `remnic_memory_store` was used over legacy `engram_memory_store`.
|
|
55
|
+
|
|
56
|
+
> Tool names: canonical name is `remnic_memory_store`. The legacy `engram_memory_store` alias remains accepted during v1.x.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: remnic-search
|
|
3
|
+
description: Run a deep full-text search across every Remnic memory. Trigger phrases include "search memories for", "find anything about", "deep search".
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- remnic_lcm_search
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
Use when `remnic-recall` did not surface something the user insists exists, or when the task genuinely needs exhaustive coverage rather than a ranked semantic summary.
|
|
11
|
+
|
|
12
|
+
Triggers:
|
|
13
|
+
|
|
14
|
+
- "Search memories for …"
|
|
15
|
+
- "Deep search on …"
|
|
16
|
+
- `/remnic:search <query>` slash command invocation.
|
|
17
|
+
- Follow-up after `remnic-recall` returned nothing useful.
|
|
18
|
+
|
|
19
|
+
## Inputs
|
|
20
|
+
|
|
21
|
+
- `query` (required) — literal phrase or keyword string; this is full-text, not semantic.
|
|
22
|
+
- Optional: date range, category filter, entity constraint.
|
|
23
|
+
|
|
24
|
+
## Procedure
|
|
25
|
+
|
|
26
|
+
1. Pick the most literal phrase the user expects to match.
|
|
27
|
+
2. Call `remnic_lcm_search` with that phrase.
|
|
28
|
+
3. Group results by date or category when the tool returns enough metadata.
|
|
29
|
+
4. Present the top 5–10 matches with dates and short excerpts.
|
|
30
|
+
5. If still nothing, say so plainly and offer `remnic-remember` for capturing the content going forward.
|
|
31
|
+
|
|
32
|
+
## Efficiency plan
|
|
33
|
+
|
|
34
|
+
- Use the most specific phrase available.
|
|
35
|
+
- One targeted search beats several broad ones.
|
|
36
|
+
- Do not re-run a deep search for the same topic in the same turn after recall already covered it.
|
|
37
|
+
|
|
38
|
+
## Pitfalls and fixes
|
|
39
|
+
|
|
40
|
+
- **Pitfall:** Using `remnic_lcm_search` as the default. **Fix:** Start with `remnic_recall`; escalate only when recall fails.
|
|
41
|
+
- **Pitfall:** Pasting huge result dumps. **Fix:** Show top matches with excerpts.
|
|
42
|
+
- **Pitfall:** Over-broad queries. **Fix:** Require at least one distinctive keyword.
|
|
43
|
+
|
|
44
|
+
## Verification checklist
|
|
45
|
+
|
|
46
|
+
- [ ] `remnic_lcm_search` was called only after recall fell short or exhaustive matching was required.
|
|
47
|
+
- [ ] Results were grouped by relevance or date when possible.
|
|
48
|
+
- [ ] Output shows excerpts, not raw payloads.
|
|
49
|
+
- [ ] Canonical `remnic_lcm_search` was used over legacy `engram_lcm_search`.
|
|
50
|
+
|
|
51
|
+
> Tool names: canonical name is `remnic_lcm_search`. The legacy `engram_lcm_search` alias remains accepted during v1.x.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: remnic-status
|
|
3
|
+
description: Check the health of the Remnic daemon, stores, and connected clients. Trigger phrases include "is remnic running", "check memory status", "daemon health".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## When to use
|
|
7
|
+
|
|
8
|
+
Use when the user asks whether Remnic is running, when recall or store calls start failing, or when diagnosing cross-agent memory issues from Claude Code.
|
|
9
|
+
|
|
10
|
+
Triggers:
|
|
11
|
+
|
|
12
|
+
- "Is Remnic running?"
|
|
13
|
+
- "Check memory status."
|
|
14
|
+
- `/remnic:status` slash command invocation.
|
|
15
|
+
- Recall/store tools returned connection errors in this turn.
|
|
16
|
+
|
|
17
|
+
## Inputs
|
|
18
|
+
|
|
19
|
+
- Optional: specific component to check (daemon, HTTP server, MCP server, store backend).
|
|
20
|
+
|
|
21
|
+
## Procedure
|
|
22
|
+
|
|
23
|
+
1. Check the Remnic health endpoint via the MCP bridge or run `remnic daemon status` in a shell.
|
|
24
|
+
2. Report, in a compact block:
|
|
25
|
+
- Daemon running state (PID if known).
|
|
26
|
+
- Listening port(s).
|
|
27
|
+
- Memory store path.
|
|
28
|
+
- Connected clients or plugins, if exposed.
|
|
29
|
+
3. If the daemon is not running, suggest `remnic daemon start` and mention the log path.
|
|
30
|
+
4. If recall/store tools were erroring earlier in the turn, correlate the health state with those errors in one sentence.
|
|
31
|
+
|
|
32
|
+
## Efficiency plan
|
|
33
|
+
|
|
34
|
+
- One health call per turn is enough; do not poll.
|
|
35
|
+
- Skip the check for trivially local tasks.
|
|
36
|
+
- Reuse the health payload for downstream troubleshooting within the same turn.
|
|
37
|
+
|
|
38
|
+
## Pitfalls and fixes
|
|
39
|
+
|
|
40
|
+
- **Pitfall:** Running `remnic daemon status` on a host where the daemon lives in a container. **Fix:** Prefer the MCP health endpoint, or run the CLI inside the container.
|
|
41
|
+
- **Pitfall:** Reporting "down" from a single failed tool call. **Fix:** Confirm with the health endpoint before claiming an outage.
|
|
42
|
+
- **Pitfall:** Forgetting the log path. **Fix:** Always include a pointer to logs on failure.
|
|
43
|
+
|
|
44
|
+
## Verification checklist
|
|
45
|
+
|
|
46
|
+
- [ ] Health was checked via the endpoint or CLI, not guessed.
|
|
47
|
+
- [ ] Daemon state, port, store path, and clients were reported concisely.
|
|
48
|
+
- [ ] If unhealthy, the user got a concrete next step.
|
|
49
|
+
- [ ] Canonical `remnic daemon` wording was used over legacy `engram daemon` where the CLI has been renamed.
|
|
50
|
+
|
|
51
|
+
> CLI names: canonical CLI is `remnic daemon`. The legacy `engram daemon` invocation remains accepted during v1.x.
|