@adverant/nexus-memory-skill 1.3.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/SKILL.md +152 -0
- package/hooks/auto-recall.sh +66 -5
- package/hooks/bead-sync.sh +596 -0
- package/hooks/episode-summary.sh +194 -176
- package/hooks/recall-memory.sh +106 -53
- package/hooks/store-memory.sh +105 -2
- package/package.json +1 -1
package/hooks/recall-memory.sh
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
#
|
|
3
|
-
# Nexus Memory -
|
|
3
|
+
# Nexus Memory - Optimized Recall Memory Hook (GraphRAG v2)
|
|
4
4
|
# Recalls relevant context from Nexus GraphRAG with entity extraction,
|
|
5
5
|
# knowledge graph traversal, and episodic memory support.
|
|
6
6
|
#
|
|
7
|
+
# PERFORMANCE OPTIMIZED:
|
|
8
|
+
# - Query caching with configurable TTL
|
|
9
|
+
# - Fast dependency checks
|
|
10
|
+
# - Connection keep-alive for faster requests
|
|
11
|
+
# - Optimized JSON parsing
|
|
12
|
+
#
|
|
7
13
|
# Usage:
|
|
8
14
|
# echo '{"query": "..."}' | recall-memory.sh
|
|
9
15
|
#
|
|
@@ -16,12 +22,13 @@
|
|
|
16
22
|
#
|
|
17
23
|
# GraphRAG Enhancement Options:
|
|
18
24
|
# NEXUS_INCLUDE_EPISODIC - Include episodic memory context (default: true)
|
|
19
|
-
# NEXUS_INCLUDE_DOCUMENTS - Include document context (default:
|
|
25
|
+
# NEXUS_INCLUDE_DOCUMENTS - Include document context (default: false for speed)
|
|
20
26
|
# NEXUS_INCLUDE_ENTITIES - Include entity information (default: true)
|
|
21
27
|
# NEXUS_INCLUDE_FACTS - Include extracted facts (default: true)
|
|
22
|
-
# NEXUS_INCLUDE_FOLLOWUPS - Include suggested follow-ups (default:
|
|
23
|
-
# NEXUS_GRAPH_DEPTH - Multi-hop graph traversal depth (default:
|
|
24
|
-
# NEXUS_MAX_TOKENS - Token budget for recall (default:
|
|
28
|
+
# NEXUS_INCLUDE_FOLLOWUPS - Include suggested follow-ups (default: false)
|
|
29
|
+
# NEXUS_GRAPH_DEPTH - Multi-hop graph traversal depth (default: 1)
|
|
30
|
+
# NEXUS_MAX_TOKENS - Token budget for recall (default: 3000)
|
|
31
|
+
# NEXUS_CACHE_TTL - Cache TTL in seconds (default: 600)
|
|
25
32
|
#
|
|
26
33
|
# Output:
|
|
27
34
|
# Rich JSON object with memories, documents, entities, facts, and suggestions
|
|
@@ -36,14 +43,19 @@ COMPANY_ID="${NEXUS_COMPANY_ID:-adverant}"
|
|
|
36
43
|
APP_ID="${NEXUS_APP_ID:-claude-code}"
|
|
37
44
|
VERBOSE="${NEXUS_VERBOSE:-0}"
|
|
38
45
|
|
|
39
|
-
# GraphRAG Enhancement Configuration
|
|
46
|
+
# GraphRAG Enhancement Configuration (optimized defaults)
|
|
40
47
|
INCLUDE_EPISODIC="${NEXUS_INCLUDE_EPISODIC:-true}"
|
|
41
|
-
INCLUDE_DOCUMENTS="${NEXUS_INCLUDE_DOCUMENTS:-
|
|
48
|
+
INCLUDE_DOCUMENTS="${NEXUS_INCLUDE_DOCUMENTS:-false}" # Disabled for speed
|
|
42
49
|
INCLUDE_ENTITIES="${NEXUS_INCLUDE_ENTITIES:-true}"
|
|
43
50
|
INCLUDE_FACTS="${NEXUS_INCLUDE_FACTS:-true}"
|
|
44
|
-
INCLUDE_FOLLOWUPS="${NEXUS_INCLUDE_FOLLOWUPS:-
|
|
45
|
-
GRAPH_DEPTH="${NEXUS_GRAPH_DEPTH:-
|
|
46
|
-
MAX_TOKENS="${NEXUS_MAX_TOKENS:-
|
|
51
|
+
INCLUDE_FOLLOWUPS="${NEXUS_INCLUDE_FOLLOWUPS:-false}" # Disabled for speed
|
|
52
|
+
GRAPH_DEPTH="${NEXUS_GRAPH_DEPTH:-1}" # Reduced for speed
|
|
53
|
+
MAX_TOKENS="${NEXUS_MAX_TOKENS:-3000}"
|
|
54
|
+
|
|
55
|
+
# Cache configuration
|
|
56
|
+
CACHE_DIR="${HOME}/.claude/session-env/recall-cache"
|
|
57
|
+
CACHE_TTL="${NEXUS_CACHE_TTL:-600}" # 10 minutes
|
|
58
|
+
SKIP_CACHE="${NEXUS_SKIP_CACHE:-0}"
|
|
47
59
|
|
|
48
60
|
# Logging function
|
|
49
61
|
log() {
|
|
@@ -66,29 +78,23 @@ EMPTY_RESPONSE='{
|
|
|
66
78
|
"suggested_followups": []
|
|
67
79
|
}'
|
|
68
80
|
|
|
81
|
+
# =========================================================
|
|
82
|
+
# FAST PATH: Early exit checks
|
|
83
|
+
# =========================================================
|
|
84
|
+
|
|
69
85
|
# Check for API key (REQUIRED)
|
|
70
86
|
if [[ -z "$NEXUS_API_KEY" ]]; then
|
|
71
|
-
log_error "NEXUS_API_KEY
|
|
72
|
-
log_error "Get your API key from: https://dashboard.adverant.ai/dashboard/api-keys"
|
|
73
|
-
log_error ""
|
|
74
|
-
log_error "Set it in your shell profile or Claude Code settings:"
|
|
75
|
-
log_error " export NEXUS_API_KEY='your-api-key-here'"
|
|
87
|
+
log_error "NEXUS_API_KEY not set"
|
|
76
88
|
echo "$EMPTY_RESPONSE"
|
|
77
89
|
exit 1
|
|
78
90
|
fi
|
|
79
91
|
|
|
80
|
-
#
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
echo "$EMPTY_RESPONSE"
|
|
84
|
-
exit 1
|
|
85
|
-
fi
|
|
92
|
+
# Fast dependency check
|
|
93
|
+
type jq &>/dev/null || { log_error "jq not available"; echo "$EMPTY_RESPONSE"; exit 1; }
|
|
94
|
+
type curl &>/dev/null || { log_error "curl not available"; echo "$EMPTY_RESPONSE"; exit 1; }
|
|
86
95
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
echo "$EMPTY_RESPONSE"
|
|
90
|
-
exit 1
|
|
91
|
-
fi
|
|
96
|
+
# Ensure cache directory exists
|
|
97
|
+
mkdir -p "$CACHE_DIR" 2>/dev/null
|
|
92
98
|
|
|
93
99
|
# Read input from stdin
|
|
94
100
|
INPUT=$(cat)
|
|
@@ -101,18 +107,24 @@ fi
|
|
|
101
107
|
|
|
102
108
|
log "Received input: ${INPUT:0:100}..."
|
|
103
109
|
|
|
104
|
-
#
|
|
105
|
-
|
|
110
|
+
# =========================================================
|
|
111
|
+
# INPUT PARSING
|
|
112
|
+
# =========================================================
|
|
106
113
|
|
|
107
|
-
#
|
|
108
|
-
LIMIT
|
|
114
|
+
# Extract query and limit with single jq call
|
|
115
|
+
read -r QUERY LIMIT < <(echo "$INPUT" | jq -r '[
|
|
116
|
+
(.query // .prompt // .tool_input.command // "recent context"),
|
|
117
|
+
(.limit // 10)
|
|
118
|
+
] | @tsv' 2>/dev/null || echo "recent context 10")
|
|
119
|
+
|
|
120
|
+
# Validate limit
|
|
109
121
|
if ! [[ "$LIMIT" =~ ^[0-9]+$ ]]; then
|
|
110
122
|
LIMIT=10
|
|
111
123
|
fi
|
|
112
124
|
|
|
113
125
|
# Get optional filters from input
|
|
114
126
|
FILTERS=$(echo "$INPUT" | jq -c '.filters // {}' 2>/dev/null)
|
|
115
|
-
if [[ "$FILTERS" == "null" ]]; then
|
|
127
|
+
if [[ "$FILTERS" == "null" ]] || [[ -z "$FILTERS" ]]; then
|
|
116
128
|
FILTERS="{}"
|
|
117
129
|
fi
|
|
118
130
|
|
|
@@ -123,7 +135,6 @@ PROJECT_DIR=$(pwd)
|
|
|
123
135
|
log "Query: $QUERY"
|
|
124
136
|
log "Limit: $LIMIT"
|
|
125
137
|
log "Project: $PROJECT_NAME"
|
|
126
|
-
log "Filters: $FILTERS"
|
|
127
138
|
|
|
128
139
|
# Skip if no query
|
|
129
140
|
if [[ -z "$QUERY" ]] || [[ "$QUERY" == "null" ]]; then
|
|
@@ -132,7 +143,34 @@ if [[ -z "$QUERY" ]] || [[ "$QUERY" == "null" ]]; then
|
|
|
132
143
|
exit 0
|
|
133
144
|
fi
|
|
134
145
|
|
|
135
|
-
#
|
|
146
|
+
# =========================================================
|
|
147
|
+
# CACHE CHECK
|
|
148
|
+
# =========================================================
|
|
149
|
+
|
|
150
|
+
# Generate query hash
|
|
151
|
+
QUERY_KEY="${PROJECT_NAME}:${QUERY:0:150}:$LIMIT"
|
|
152
|
+
QUERY_HASH=$(echo -n "$QUERY_KEY" | md5 2>/dev/null || echo -n "$QUERY_KEY" | md5sum 2>/dev/null | cut -d' ' -f1)
|
|
153
|
+
CACHE_FILE="${CACHE_DIR}/recall-${QUERY_HASH}"
|
|
154
|
+
|
|
155
|
+
# Check cache
|
|
156
|
+
if [[ "$SKIP_CACHE" != "1" ]] && [[ -f "$CACHE_FILE" ]]; then
|
|
157
|
+
CACHE_AGE=$(( $(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0) ))
|
|
158
|
+
|
|
159
|
+
if [[ "$CACHE_AGE" -lt "$CACHE_TTL" ]]; then
|
|
160
|
+
log "Cache hit (age: ${CACHE_AGE}s)"
|
|
161
|
+
cat "$CACHE_FILE"
|
|
162
|
+
exit 0
|
|
163
|
+
else
|
|
164
|
+
log "Cache expired"
|
|
165
|
+
rm -f "$CACHE_FILE" 2>/dev/null
|
|
166
|
+
fi
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# =========================================================
|
|
170
|
+
# API REQUEST
|
|
171
|
+
# =========================================================
|
|
172
|
+
|
|
173
|
+
# Build the optimized payload for GraphRAG retrieval
|
|
136
174
|
PAYLOAD=$(jq -n \
|
|
137
175
|
--arg query "$QUERY" \
|
|
138
176
|
--argjson limit "$LIMIT" \
|
|
@@ -159,42 +197,40 @@ PAYLOAD=$(jq -n \
|
|
|
159
197
|
include_entities: $includeEntities,
|
|
160
198
|
include_facts: $includeFacts,
|
|
161
199
|
include_followups: $includeFollowups,
|
|
162
|
-
enable_graph_traversal:
|
|
200
|
+
enable_graph_traversal: ($graphDepth > 0),
|
|
163
201
|
graph_depth: $graphDepth,
|
|
164
202
|
max_tokens: $maxTokens,
|
|
165
203
|
hybrid_search: true,
|
|
166
|
-
rerank: true
|
|
204
|
+
rerank: true,
|
|
205
|
+
fast_mode: true
|
|
167
206
|
}')
|
|
168
207
|
|
|
169
|
-
# Use enhanced retrieval endpoint
|
|
170
|
-
# The Gateway exposes this at /api/retrieve/enhanced (routes through smartRouter)
|
|
208
|
+
# Use enhanced retrieval endpoint
|
|
171
209
|
ENDPOINT="$NEXUS_API_URL/api/retrieve/enhanced"
|
|
172
210
|
log "Recalling from $ENDPOINT"
|
|
173
211
|
|
|
174
|
-
#
|
|
175
|
-
# Authorization: Bearer token for API authentication
|
|
176
|
-
# Headers: X-Company-ID, X-App-ID, X-User-ID (required for tenant context)
|
|
212
|
+
# Make request with optimized settings
|
|
177
213
|
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "$ENDPOINT" \
|
|
178
214
|
-H "Content-Type: application/json" \
|
|
179
215
|
-H "Authorization: Bearer $NEXUS_API_KEY" \
|
|
180
216
|
-H "X-Company-ID: $COMPANY_ID" \
|
|
181
217
|
-H "X-App-ID: $APP_ID" \
|
|
182
218
|
-H "X-User-ID: ${USER:-unknown}" \
|
|
219
|
+
-H "Connection: keep-alive" \
|
|
183
220
|
-d "$PAYLOAD" \
|
|
184
|
-
--
|
|
221
|
+
--connect-timeout 3 \
|
|
222
|
+
--max-time 10 2>&1)
|
|
185
223
|
|
|
186
224
|
# Parse response
|
|
187
225
|
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
|
188
226
|
BODY=$(echo "$RESPONSE" | sed '$d')
|
|
189
227
|
|
|
190
228
|
log "Response code: $HTTP_CODE"
|
|
191
|
-
log "Response body: ${BODY:0:500}..."
|
|
192
229
|
|
|
193
|
-
#
|
|
230
|
+
# Fallback to basic endpoint if enhanced fails
|
|
194
231
|
if [[ "$HTTP_CODE" != "200" ]]; then
|
|
195
232
|
log "Enhanced retrieval failed (HTTP $HTTP_CODE), trying basic endpoint..."
|
|
196
233
|
|
|
197
|
-
# Fallback to basic recall endpoint
|
|
198
234
|
BASIC_PAYLOAD=$(jq -n \
|
|
199
235
|
--arg query "$QUERY" \
|
|
200
236
|
--argjson limit "$LIMIT" \
|
|
@@ -209,8 +245,10 @@ if [[ "$HTTP_CODE" != "200" ]]; then
|
|
|
209
245
|
-H "X-Company-ID: $COMPANY_ID" \
|
|
210
246
|
-H "X-App-ID: $APP_ID" \
|
|
211
247
|
-H "X-User-ID: ${USER:-unknown}" \
|
|
248
|
+
-H "Connection: keep-alive" \
|
|
212
249
|
-d "$BASIC_PAYLOAD" \
|
|
213
|
-
--
|
|
250
|
+
--connect-timeout 2 \
|
|
251
|
+
--max-time 8 2>&1)
|
|
214
252
|
|
|
215
253
|
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
|
216
254
|
BODY=$(echo "$RESPONSE" | sed '$d')
|
|
@@ -225,16 +263,19 @@ if [[ "$HTTP_CODE" != "200" ]]; then
|
|
|
225
263
|
fi
|
|
226
264
|
|
|
227
265
|
# Validate JSON response
|
|
228
|
-
if ! echo "$BODY" | jq . &>/dev/null; then
|
|
266
|
+
if ! echo "$BODY" | jq -e . &>/dev/null; then
|
|
229
267
|
log_error "Invalid JSON response"
|
|
230
268
|
echo "$EMPTY_RESPONSE"
|
|
231
269
|
exit 0
|
|
232
270
|
fi
|
|
233
271
|
|
|
234
|
-
#
|
|
235
|
-
#
|
|
272
|
+
# =========================================================
|
|
273
|
+
# RESPONSE NORMALIZATION
|
|
274
|
+
# =========================================================
|
|
275
|
+
|
|
276
|
+
# Normalize response to expected structure - single jq pass
|
|
236
277
|
NORMALIZED=$(echo "$BODY" | jq '
|
|
237
|
-
#
|
|
278
|
+
# Handle Gateway wrapper
|
|
238
279
|
(if .data.results then .data.results else . end) as $result |
|
|
239
280
|
{
|
|
240
281
|
memories: ($result.unified_memories // $result.memories // []),
|
|
@@ -244,13 +285,25 @@ NORMALIZED=$(echo "$BODY" | jq '
|
|
|
244
285
|
episodic_context: ($result.episodic_context // []),
|
|
245
286
|
suggested_followups: ($result.suggested_followups // []),
|
|
246
287
|
query_understanding: ($result.query_understanding // null),
|
|
247
|
-
confidence_score: ($result.confidence_score // null)
|
|
288
|
+
confidence_score: ($result.confidence_score // null),
|
|
289
|
+
context: {
|
|
290
|
+
project: ($result.context.project // "unknown"),
|
|
291
|
+
query: ($result.query // "")
|
|
292
|
+
}
|
|
248
293
|
}
|
|
249
294
|
')
|
|
250
295
|
|
|
251
|
-
#
|
|
296
|
+
# Cache the result
|
|
297
|
+
if [[ "$SKIP_CACHE" != "1" ]]; then
|
|
298
|
+
echo "$NORMALIZED" > "$CACHE_FILE" 2>/dev/null
|
|
299
|
+
log "Cached result for ${CACHE_TTL}s"
|
|
300
|
+
|
|
301
|
+
# Cleanup old cache files (async)
|
|
302
|
+
(find "$CACHE_DIR" -type f -name "recall-*" -mmin +$((CACHE_TTL / 60 + 1)) -delete 2>/dev/null &)
|
|
303
|
+
fi
|
|
304
|
+
|
|
305
|
+
# Output verbose info if enabled
|
|
252
306
|
if [[ "$VERBOSE" == "1" ]]; then
|
|
253
|
-
# In verbose mode, include additional metadata
|
|
254
307
|
MEMORY_COUNT=$(echo "$NORMALIZED" | jq '.memories | length')
|
|
255
308
|
ENTITY_COUNT=$(echo "$NORMALIZED" | jq '.entities | length')
|
|
256
309
|
FACT_COUNT=$(echo "$NORMALIZED" | jq '.facts | length')
|
|
@@ -258,4 +311,4 @@ if [[ "$VERBOSE" == "1" ]]; then
|
|
|
258
311
|
log "Retrieved: $MEMORY_COUNT memories, $ENTITY_COUNT entities, $FACT_COUNT facts"
|
|
259
312
|
fi
|
|
260
313
|
|
|
261
|
-
echo "$NORMALIZED"
|
|
314
|
+
echo "$NORMALIZED"
|
package/hooks/store-memory.sh
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
# pattern - Reusable code patterns
|
|
28
28
|
# preference - User/project preferences
|
|
29
29
|
# context - Project-specific context
|
|
30
|
+
# bead - Git-backed issue/task tracking (synced via bead-sync.sh)
|
|
30
31
|
#
|
|
31
32
|
|
|
32
33
|
set -o pipefail
|
|
@@ -84,6 +85,85 @@ detect_domain() {
|
|
|
84
85
|
fi
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
# Classify content to determine if it should be stored
|
|
89
|
+
# Returns: bead | decision | learning | error_fix | code_change | routine | noise
|
|
90
|
+
classify_content() {
|
|
91
|
+
local content="$1"
|
|
92
|
+
local event_type="$2"
|
|
93
|
+
local content_lower=$(echo "$content" | tr '[:upper:]' '[:lower:]')
|
|
94
|
+
|
|
95
|
+
# Bead operations (HIGH priority - always store + trigger sync)
|
|
96
|
+
# Matches: bd create, bd close, bd work, bd dep, bd list, bd ready, bd show, bd init
|
|
97
|
+
if echo "$content" | grep -qE '\bbd (create|close|work|dep|list|ready|show|init|update|add|remove|link|unlink|graph|molecule)\b'; then
|
|
98
|
+
echo "bead"
|
|
99
|
+
return 0
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# Also detect bead-related event type
|
|
103
|
+
if [[ "$event_type" == "bead" ]]; then
|
|
104
|
+
echo "bead"
|
|
105
|
+
return 0
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# High-value patterns - ALWAYS store
|
|
109
|
+
# Decisions: chose X over Y, decided to, instead of, went with
|
|
110
|
+
if echo "$content_lower" | grep -qE '(decided to|chose|instead of|went with|opted for|prefer|selected|picked)'; then
|
|
111
|
+
echo "decision"
|
|
112
|
+
return 0
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
# Learnings: learned, realized, discovered, found out, turns out
|
|
116
|
+
if echo "$content_lower" | grep -qE '(learned|realized|discovered|found out|turns out|figured out|now i know|aha|insight)'; then
|
|
117
|
+
echo "learning"
|
|
118
|
+
return 0
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
# Error fixes: fixed, resolved, solved, bug, error, issue
|
|
122
|
+
if echo "$content_lower" | grep -qE '(fixed|resolved|solved|the (bug|error|issue)|fix for|solution)'; then
|
|
123
|
+
echo "error_fix"
|
|
124
|
+
return 0
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
# Event type overrides for explicit categorization
|
|
128
|
+
if [[ "$event_type" == "decision" ]] || [[ "$event_type" == "preference" ]]; then
|
|
129
|
+
echo "decision"
|
|
130
|
+
return 0
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
if [[ "$event_type" == "learning" ]]; then
|
|
134
|
+
echo "learning"
|
|
135
|
+
return 0
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
if [[ "$event_type" == "fix" ]] || [[ "$event_type" == "pattern" ]]; then
|
|
139
|
+
echo "error_fix"
|
|
140
|
+
return 0
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
# Medium-value: code changes with explanation
|
|
144
|
+
if echo "$content_lower" | grep -qE '(modified|updated|changed|added|implemented|created|refactored)'; then
|
|
145
|
+
echo "code_change"
|
|
146
|
+
return 0
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
# Low-value: routine commands - SKIP storage
|
|
150
|
+
# Common shell commands, git status checks, npm installs
|
|
151
|
+
if echo "$content" | grep -qE '^(ls |cd |pwd|cat |head |tail |grep |find |git status|git diff|git log|npm install|npm run|yarn |pnpm )'; then
|
|
152
|
+
echo "routine"
|
|
153
|
+
return 0
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# Noise: very short content, single words, numbers only
|
|
157
|
+
if [[ ${#content} -lt 30 ]]; then
|
|
158
|
+
echo "noise"
|
|
159
|
+
return 0
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
# Default: store as context
|
|
163
|
+
echo "context"
|
|
164
|
+
return 0
|
|
165
|
+
}
|
|
166
|
+
|
|
87
167
|
# Get previous memory ID for causal chaining
|
|
88
168
|
get_previous_memory_id() {
|
|
89
169
|
if [[ -f "$LAST_MEMORY_FILE" ]]; then
|
|
@@ -142,7 +222,7 @@ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"' 2>/dev/null)
|
|
|
142
222
|
EVENT_TYPE=$(echo "$INPUT" | jq -r '.event_type // "conversation"' 2>/dev/null)
|
|
143
223
|
|
|
144
224
|
# Validate event type
|
|
145
|
-
VALID_TYPES=("fix" "decision" "learning" "pattern" "preference" "context" "conversation")
|
|
225
|
+
VALID_TYPES=("fix" "decision" "learning" "pattern" "preference" "context" "conversation" "bead")
|
|
146
226
|
TYPE_VALID=0
|
|
147
227
|
for type in "${VALID_TYPES[@]}"; do
|
|
148
228
|
if [[ "$EVENT_TYPE" == "$type" ]]; then
|
|
@@ -169,6 +249,16 @@ if [[ -z "$CONTENT" ]] || [[ "$CONTENT" == "null" ]]; then
|
|
|
169
249
|
exit 0
|
|
170
250
|
fi
|
|
171
251
|
|
|
252
|
+
# Classify content to determine storage value
|
|
253
|
+
CONTENT_CLASS=$(classify_content "$CONTENT" "$EVENT_TYPE")
|
|
254
|
+
log "Content classification: $CONTENT_CLASS"
|
|
255
|
+
|
|
256
|
+
# Skip low-value content (routine commands, noise)
|
|
257
|
+
if [[ "$CONTENT_CLASS" == "routine" ]] || [[ "$CONTENT_CLASS" == "noise" ]]; then
|
|
258
|
+
log "Skipping $CONTENT_CLASS content (low value)"
|
|
259
|
+
exit 0
|
|
260
|
+
fi
|
|
261
|
+
|
|
172
262
|
# Truncate very long content (max 10000 chars)
|
|
173
263
|
if [[ ${#CONTENT} -gt 10000 ]]; then
|
|
174
264
|
CONTENT="${CONTENT:0:10000}... [truncated]"
|
|
@@ -196,6 +286,7 @@ PAYLOAD=$(jq -n \
|
|
|
196
286
|
--arg content "$CONTENT" \
|
|
197
287
|
--arg session "$SESSION_ID" \
|
|
198
288
|
--arg event "$EVENT_TYPE" \
|
|
289
|
+
--arg contentClass "$CONTENT_CLASS" \
|
|
199
290
|
--arg project "$PROJECT_NAME" \
|
|
200
291
|
--arg projectDir "$PROJECT_DIR" \
|
|
201
292
|
--arg timestamp "$TIMESTAMP" \
|
|
@@ -207,10 +298,11 @@ PAYLOAD=$(jq -n \
|
|
|
207
298
|
--argjson entityTypes "$ENTITY_TYPES_JSON" \
|
|
208
299
|
'{
|
|
209
300
|
content: $content,
|
|
210
|
-
tags: ["claude-code", "session:\($session)", "type:\($event)", "project:\($project)", "domain:\($domain)"],
|
|
301
|
+
tags: ["claude-code", "session:\($session)", "type:\($event)", "class:\($contentClass)", "project:\($project)", "domain:\($domain)"],
|
|
211
302
|
metadata: {
|
|
212
303
|
sessionId: $session,
|
|
213
304
|
eventType: $event,
|
|
305
|
+
contentClass: $contentClass,
|
|
214
306
|
projectDir: $projectDir,
|
|
215
307
|
projectName: $project,
|
|
216
308
|
timestamp: $timestamp,
|
|
@@ -288,4 +380,15 @@ else
|
|
|
288
380
|
) &
|
|
289
381
|
fi
|
|
290
382
|
|
|
383
|
+
# Trigger bead-sync if this was a bead operation
|
|
384
|
+
# This syncs the local beads to GraphRAG in the background
|
|
385
|
+
if [[ "$CONTENT_CLASS" == "bead" ]]; then
|
|
386
|
+
BEAD_SYNC_HOOK="${HOME}/.claude/hooks/bead-sync.sh"
|
|
387
|
+
if [[ -x "$BEAD_SYNC_HOOK" ]]; then
|
|
388
|
+
log "Triggering bead-sync for bead operation"
|
|
389
|
+
# Run sync-latest in background to not block the hook
|
|
390
|
+
("$BEAD_SYNC_HOOK" sync-latest 2>/dev/null) &
|
|
391
|
+
fi
|
|
392
|
+
fi
|
|
393
|
+
|
|
291
394
|
exit 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adverant/nexus-memory-skill",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Claude Code skill for persistent memory via Nexus GraphRAG - store and recall memories across all sessions and projects",
|
|
5
5
|
"main": "SKILL.md",
|
|
6
6
|
"type": "module",
|