@memoire-ai/collector 0.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/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-test.log +45 -0
- package/cursor-hooks/README.md +119 -0
- package/cursor-hooks/context-inject.sh +118 -0
- package/cursor-hooks/hooks.json +39 -0
- package/cursor-hooks/save-file-edit.sh +130 -0
- package/cursor-hooks/save-observation.sh +116 -0
- package/cursor-hooks/save-shell-execution.sh +121 -0
- package/cursor-hooks/session-summary.sh +142 -0
- package/dist/capture.d.ts +111 -0
- package/dist/capture.d.ts.map +1 -0
- package/dist/capture.integration.d.ts +2 -0
- package/dist/capture.integration.d.ts.map +1 -0
- package/dist/capture.integration.js +67 -0
- package/dist/capture.integration.js.map +1 -0
- package/dist/capture.js +264 -0
- package/dist/capture.js.map +1 -0
- package/dist/client-summarizer.d.ts +59 -0
- package/dist/client-summarizer.d.ts.map +1 -0
- package/dist/client-summarizer.js +211 -0
- package/dist/client-summarizer.js.map +1 -0
- package/dist/client-summarizer.test.d.ts +2 -0
- package/dist/client-summarizer.test.d.ts.map +1 -0
- package/dist/client-summarizer.test.js +127 -0
- package/dist/client-summarizer.test.js.map +1 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +131 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.d.ts +2 -0
- package/dist/config.test.d.ts.map +1 -0
- package/dist/config.test.js +182 -0
- package/dist/config.test.js.map +1 -0
- package/dist/cursor-hooks.d.ts +46 -0
- package/dist/cursor-hooks.d.ts.map +1 -0
- package/dist/cursor-hooks.js +251 -0
- package/dist/cursor-hooks.js.map +1 -0
- package/dist/cursor-rules.d.ts +42 -0
- package/dist/cursor-rules.d.ts.map +1 -0
- package/dist/cursor-rules.js +229 -0
- package/dist/cursor-rules.js.map +1 -0
- package/dist/cursor-rules.test.d.ts +2 -0
- package/dist/cursor-rules.test.d.ts.map +1 -0
- package/dist/cursor-rules.test.js +55 -0
- package/dist/cursor-rules.test.js.map +1 -0
- package/dist/dedup.d.ts +22 -0
- package/dist/dedup.d.ts.map +1 -0
- package/dist/dedup.js +60 -0
- package/dist/dedup.js.map +1 -0
- package/dist/dedup.test.d.ts +2 -0
- package/dist/dedup.test.d.ts.map +1 -0
- package/dist/dedup.test.js +83 -0
- package/dist/dedup.test.js.map +1 -0
- package/dist/hooks/index.d.ts +52 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +136 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks.test.d.ts +2 -0
- package/dist/hooks.test.d.ts.map +1 -0
- package/dist/hooks.test.js +94 -0
- package/dist/hooks.test.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/strip-private.d.ts +12 -0
- package/dist/strip-private.d.ts.map +1 -0
- package/dist/strip-private.js +28 -0
- package/dist/strip-private.js.map +1 -0
- package/dist/strip-private.test.d.ts +2 -0
- package/dist/strip-private.test.d.ts.map +1 -0
- package/dist/strip-private.test.js +37 -0
- package/dist/strip-private.test.js.map +1 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +11 -0
- package/dist/utils.js.map +1 -0
- package/package.json +28 -0
- package/src/capture.integration.ts +98 -0
- package/src/capture.ts +352 -0
- package/src/client-summarizer.test.ts +144 -0
- package/src/client-summarizer.ts +338 -0
- package/src/config.test.ts +211 -0
- package/src/config.ts +157 -0
- package/src/cursor-hooks.ts +309 -0
- package/src/cursor-rules.test.ts +63 -0
- package/src/cursor-rules.ts +313 -0
- package/src/dedup.test.ts +84 -0
- package/src/dedup.ts +67 -0
- package/src/hooks/index.ts +226 -0
- package/src/hooks.test.ts +111 -0
- package/src/index.ts +32 -0
- package/src/strip-private.test.ts +57 -0
- package/src/strip-private.ts +34 -0
- package/src/utils.ts +10 -0
- package/tsconfig.json +12 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Memoire shell execution hook for Cursor IDE
|
|
3
|
+
# Called via afterShellExecution — records shell commands as observation events
|
|
4
|
+
# so Memoire can track what tools/commands the developer is running.
|
|
5
|
+
#
|
|
6
|
+
# Usage: sh save-shell-execution.sh ["command"] ["exit_code"]
|
|
7
|
+
#
|
|
8
|
+
# Arguments:
|
|
9
|
+
# $1 - Shell command that was executed
|
|
10
|
+
# $2 - Exit code (0 = success, non-zero = failure)
|
|
11
|
+
#
|
|
12
|
+
# Environment:
|
|
13
|
+
# MEMOIRE_API_URL - API base URL (default: http://127.0.0.1:3100)
|
|
14
|
+
# MEMOIRE_API_KEY - API key for authentication
|
|
15
|
+
# MEMOIRE_ORG_ID - Organization ID
|
|
16
|
+
# MEMOIRE_PROJECT_ID - Project ID
|
|
17
|
+
# MEMOIRE_USER_ID - User ID
|
|
18
|
+
# MEMOIRE_SESSION_ID - Session ID (auto-generated if unset)
|
|
19
|
+
#
|
|
20
|
+
# Falls back to ~/.memoire/config for any unset values.
|
|
21
|
+
# Fails silently — never blocks the IDE.
|
|
22
|
+
|
|
23
|
+
set -e
|
|
24
|
+
|
|
25
|
+
COMMAND="${1:-unknown}"
|
|
26
|
+
EXIT_CODE="${2:-0}"
|
|
27
|
+
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
# Config loading (shared pattern with save-observation.sh)
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
CONFIG_FILE="${MEMOIRE_CONFIG_FILE:-$HOME/.memoire/config}"
|
|
33
|
+
|
|
34
|
+
load_config() {
|
|
35
|
+
if [ -f "$CONFIG_FILE" ]; then
|
|
36
|
+
while IFS='=' read -r key value; do
|
|
37
|
+
case "$key" in
|
|
38
|
+
''|\#*) continue ;;
|
|
39
|
+
esac
|
|
40
|
+
value=$(printf '%s' "$value" | sed "s/^[[:space:]]*[\"']\\{0,1\\}//;s/[\"']\\{0,1\\}[[:space:]]*$//")
|
|
41
|
+
case "$key" in
|
|
42
|
+
MEMOIRE_API_URL) [ -z "$MEMOIRE_API_URL" ] && MEMOIRE_API_URL="$value" ;;
|
|
43
|
+
MEMOIRE_API_KEY) [ -z "$MEMOIRE_API_KEY" ] && MEMOIRE_API_KEY="$value" ;;
|
|
44
|
+
MEMOIRE_ORG_ID) [ -z "$MEMOIRE_ORG_ID" ] && MEMOIRE_ORG_ID="$value" ;;
|
|
45
|
+
MEMOIRE_PROJECT_ID) [ -z "$MEMOIRE_PROJECT_ID" ] && MEMOIRE_PROJECT_ID="$value" ;;
|
|
46
|
+
MEMOIRE_USER_ID) [ -z "$MEMOIRE_USER_ID" ] && MEMOIRE_USER_ID="$value" ;;
|
|
47
|
+
MEMOIRE_SESSION_ID) [ -z "$MEMOIRE_SESSION_ID" ] && MEMOIRE_SESSION_ID="$value" ;;
|
|
48
|
+
MEMOIRE_CLIENT) [ -z "$MEMOIRE_CLIENT" ] && MEMOIRE_CLIENT="$value" ;;
|
|
49
|
+
esac
|
|
50
|
+
done < "$CONFIG_FILE"
|
|
51
|
+
fi
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
load_config
|
|
55
|
+
|
|
56
|
+
API_URL="${MEMOIRE_API_URL:-http://127.0.0.1:3100}"
|
|
57
|
+
API_KEY="${MEMOIRE_API_KEY:-}"
|
|
58
|
+
ORG_ID="${MEMOIRE_ORG_ID:-}"
|
|
59
|
+
PROJECT_ID="${MEMOIRE_PROJECT_ID:-}"
|
|
60
|
+
USER_ID="${MEMOIRE_USER_ID:-}"
|
|
61
|
+
SESSION_ID="${MEMOIRE_SESSION_ID:-cursor-$(date +%s)-$$}"
|
|
62
|
+
CLIENT="${MEMOIRE_CLIENT:-cursor}"
|
|
63
|
+
|
|
64
|
+
if [ -z "$ORG_ID" ] || [ -z "$PROJECT_ID" ] || [ -z "$USER_ID" ]; then
|
|
65
|
+
exit 0
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
# Classify event based on exit code
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
if [ "$EXIT_CODE" != "0" ]; then
|
|
73
|
+
EVENT_TYPE="attempt"
|
|
74
|
+
CONTENT="Shell command failed (exit ${EXIT_CODE}): ${COMMAND}"
|
|
75
|
+
else
|
|
76
|
+
EVENT_TYPE="observation"
|
|
77
|
+
CONTENT="Shell command executed: ${COMMAND}"
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Truncate very long commands (keep first 500 chars)
|
|
81
|
+
if [ ${#CONTENT} -gt 500 ]; then
|
|
82
|
+
CONTENT=$(printf '%.500s…' "$CONTENT")
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# ---------------------------------------------------------------------------
|
|
86
|
+
# Send event to Memoire API
|
|
87
|
+
# ---------------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
AUTH_HEADER=""
|
|
90
|
+
if [ -n "$API_KEY" ]; then
|
|
91
|
+
AUTH_HEADER="Authorization: Bearer ${API_KEY}"
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
95
|
+
IDEMPOTENCY_KEY="shell-${SESSION_ID}-$(date +%s)"
|
|
96
|
+
|
|
97
|
+
# Escape command for safe JSON embedding
|
|
98
|
+
SAFE_CONTENT=$(printf '%s' "$CONTENT" | sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g')
|
|
99
|
+
|
|
100
|
+
curl -sf --max-time 3 \
|
|
101
|
+
-X POST \
|
|
102
|
+
-H "Content-Type: application/json" \
|
|
103
|
+
${AUTH_HEADER:+-H "$AUTH_HEADER"} \
|
|
104
|
+
-d "{
|
|
105
|
+
\"events\": [{
|
|
106
|
+
\"org_id\": \"${ORG_ID}\",
|
|
107
|
+
\"project_id\": \"${PROJECT_ID}\",
|
|
108
|
+
\"user_id\": \"${USER_ID}\",
|
|
109
|
+
\"session_id\": \"${SESSION_ID}\",
|
|
110
|
+
\"client\": \"${CLIENT}\",
|
|
111
|
+
\"event_type\": \"${EVENT_TYPE}\",
|
|
112
|
+
\"content\": \"${SAFE_CONTENT}\",
|
|
113
|
+
\"concepts\": [\"shell\", \"command\"],
|
|
114
|
+
\"created_at\": \"${TIMESTAMP}\",
|
|
115
|
+
\"idempotency_key\": \"${IDEMPOTENCY_KEY}\",
|
|
116
|
+
\"private\": false
|
|
117
|
+
}]
|
|
118
|
+
}" \
|
|
119
|
+
"${API_URL}/v1/events" >/dev/null 2>&1 || true
|
|
120
|
+
|
|
121
|
+
exit 0
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Memoire session summary hook for Cursor IDE
|
|
3
|
+
# Called via the stop hook — tells Memoire the session ended so it can
|
|
4
|
+
# generate a summary and update the project profile.
|
|
5
|
+
#
|
|
6
|
+
# Usage: sh session-summary.sh
|
|
7
|
+
#
|
|
8
|
+
# Environment:
|
|
9
|
+
# MEMOIRE_API_URL - API base URL (default: http://127.0.0.1:3100)
|
|
10
|
+
# MEMOIRE_API_KEY - API key for authentication
|
|
11
|
+
# MEMOIRE_ORG_ID - Organization ID
|
|
12
|
+
# MEMOIRE_PROJECT_ID - Project ID
|
|
13
|
+
# MEMOIRE_USER_ID - User ID
|
|
14
|
+
# MEMOIRE_SESSION_ID - Session ID (auto-generated if unset)
|
|
15
|
+
#
|
|
16
|
+
# Falls back to ~/.memoire/config for any unset values.
|
|
17
|
+
# Fails silently — never blocks the IDE.
|
|
18
|
+
|
|
19
|
+
set -e
|
|
20
|
+
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
# Config loading
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
CONFIG_FILE="${MEMOIRE_CONFIG_FILE:-$HOME/.memoire/config}"
|
|
26
|
+
|
|
27
|
+
load_config() {
|
|
28
|
+
if [ -f "$CONFIG_FILE" ]; then
|
|
29
|
+
while IFS='=' read -r key value; do
|
|
30
|
+
case "$key" in
|
|
31
|
+
''|\#*) continue ;;
|
|
32
|
+
esac
|
|
33
|
+
value=$(printf '%s' "$value" | sed "s/^[[:space:]]*[\"']\\{0,1\\}//;s/[\"']\\{0,1\\}[[:space:]]*$//")
|
|
34
|
+
case "$key" in
|
|
35
|
+
MEMOIRE_API_URL) [ -z "$MEMOIRE_API_URL" ] && MEMOIRE_API_URL="$value" ;;
|
|
36
|
+
MEMOIRE_API_KEY) [ -z "$MEMOIRE_API_KEY" ] && MEMOIRE_API_KEY="$value" ;;
|
|
37
|
+
MEMOIRE_ORG_ID) [ -z "$MEMOIRE_ORG_ID" ] && MEMOIRE_ORG_ID="$value" ;;
|
|
38
|
+
MEMOIRE_PROJECT_ID) [ -z "$MEMOIRE_PROJECT_ID" ] && MEMOIRE_PROJECT_ID="$value" ;;
|
|
39
|
+
MEMOIRE_USER_ID) [ -z "$MEMOIRE_USER_ID" ] && MEMOIRE_USER_ID="$value" ;;
|
|
40
|
+
MEMOIRE_SESSION_ID) [ -z "$MEMOIRE_SESSION_ID" ] && MEMOIRE_SESSION_ID="$value" ;;
|
|
41
|
+
MEMOIRE_CLIENT) [ -z "$MEMOIRE_CLIENT" ] && MEMOIRE_CLIENT="$value" ;;
|
|
42
|
+
esac
|
|
43
|
+
done < "$CONFIG_FILE"
|
|
44
|
+
fi
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
load_config
|
|
48
|
+
|
|
49
|
+
API_URL="${MEMOIRE_API_URL:-http://127.0.0.1:3100}"
|
|
50
|
+
API_KEY="${MEMOIRE_API_KEY:-}"
|
|
51
|
+
ORG_ID="${MEMOIRE_ORG_ID:-}"
|
|
52
|
+
PROJECT_ID="${MEMOIRE_PROJECT_ID:-}"
|
|
53
|
+
USER_ID="${MEMOIRE_USER_ID:-}"
|
|
54
|
+
SESSION_ID="${MEMOIRE_SESSION_ID:-cursor-$(date +%s)-$$}"
|
|
55
|
+
CLIENT="${MEMOIRE_CLIENT:-cursor}"
|
|
56
|
+
|
|
57
|
+
if [ -z "$ORG_ID" ] || [ -z "$PROJECT_ID" ] || [ -z "$USER_ID" ]; then
|
|
58
|
+
exit 0
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
# Send session_summary event
|
|
63
|
+
# ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
AUTH_HEADER=""
|
|
66
|
+
if [ -n "$API_KEY" ]; then
|
|
67
|
+
AUTH_HEADER="Authorization: Bearer ${API_KEY}"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
71
|
+
IDEMPOTENCY_KEY="session-end-${SESSION_ID}-$(date +%s)"
|
|
72
|
+
|
|
73
|
+
curl -sf --max-time 3 \
|
|
74
|
+
-X POST \
|
|
75
|
+
-H "Content-Type: application/json" \
|
|
76
|
+
${AUTH_HEADER:+-H "$AUTH_HEADER"} \
|
|
77
|
+
-d "{
|
|
78
|
+
\"events\": [{
|
|
79
|
+
\"org_id\": \"${ORG_ID}\",
|
|
80
|
+
\"project_id\": \"${PROJECT_ID}\",
|
|
81
|
+
\"user_id\": \"${USER_ID}\",
|
|
82
|
+
\"session_id\": \"${SESSION_ID}\",
|
|
83
|
+
\"client\": \"${CLIENT}\",
|
|
84
|
+
\"event_type\": \"session_summary\",
|
|
85
|
+
\"content\": \"Cursor session ended (hook-generated)\",
|
|
86
|
+
\"created_at\": \"${TIMESTAMP}\",
|
|
87
|
+
\"idempotency_key\": \"${IDEMPOTENCY_KEY}\",
|
|
88
|
+
\"private\": false
|
|
89
|
+
}]
|
|
90
|
+
}" \
|
|
91
|
+
"${API_URL}/v1/events" >/dev/null 2>&1 || true
|
|
92
|
+
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
# Fetch and update dashboard summary for context file refresh
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
SUMMARY_RESPONSE=$(curl -sf --max-time 3 \
|
|
98
|
+
-X POST \
|
|
99
|
+
-H "Content-Type: application/json" \
|
|
100
|
+
${AUTH_HEADER:+-H "$AUTH_HEADER"} \
|
|
101
|
+
-d "{
|
|
102
|
+
\"org_id\": \"${ORG_ID}\",
|
|
103
|
+
\"project_id\": \"${PROJECT_ID}\",
|
|
104
|
+
\"viewer_user_id\": \"${USER_ID}\",
|
|
105
|
+
\"recent_limit\": 10,
|
|
106
|
+
\"recent_hours\": 24
|
|
107
|
+
}" \
|
|
108
|
+
"${API_URL}/v1/dashboard/summary" 2>/dev/null) || exit 0
|
|
109
|
+
|
|
110
|
+
# Extract a brief summary text for the context file
|
|
111
|
+
SUMMARY_TEXT=$(printf '%s' "$SUMMARY_RESPONSE" | sed -n 's/.*"summary"[[:space:]]*:[[:space:]]*"//p' | sed 's/".*//' | sed 's/\\n/\
|
|
112
|
+
/g; s/\\"/"/g; s/\\\\/\\/g')
|
|
113
|
+
|
|
114
|
+
if [ -z "$SUMMARY_TEXT" ]; then
|
|
115
|
+
exit 0
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# Update the cursor rules file with latest summary
|
|
119
|
+
RULES_DIR=".cursor/rules"
|
|
120
|
+
RULES_FILE="${RULES_DIR}/memoire-context.mdc"
|
|
121
|
+
TEMP_FILE="${RULES_FILE}.tmp"
|
|
122
|
+
|
|
123
|
+
mkdir -p "$RULES_DIR"
|
|
124
|
+
|
|
125
|
+
cat > "$TEMP_FILE" << 'FRONTMATTER'
|
|
126
|
+
---
|
|
127
|
+
alwaysApply: true
|
|
128
|
+
description: "Memoire shared memory context (auto-updated by hook)"
|
|
129
|
+
---
|
|
130
|
+
FRONTMATTER
|
|
131
|
+
|
|
132
|
+
printf '\n# Memoire Context\n\n' >> "$TEMP_FILE"
|
|
133
|
+
printf '> Auto-updated by Memoire session-summary hook.\n' >> "$TEMP_FILE"
|
|
134
|
+
printf '> Use Memoire MCP tools for deeper queries.\n\n' >> "$TEMP_FILE"
|
|
135
|
+
printf '## Recent Session Summary\n\n' >> "$TEMP_FILE"
|
|
136
|
+
printf '%s\n' "$SUMMARY_TEXT" >> "$TEMP_FILE"
|
|
137
|
+
printf '\n---\n' >> "$TEMP_FILE"
|
|
138
|
+
printf '*Use Memoire MCP tools (`search_context`, `project_profile`, `oracle_research`) for detailed queries.*\n' >> "$TEMP_FILE"
|
|
139
|
+
|
|
140
|
+
mv "$TEMP_FILE" "$RULES_FILE"
|
|
141
|
+
|
|
142
|
+
exit 0
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { MemoireClient as ClientType } from '@memoire-ai/shared';
|
|
2
|
+
import { type ClientSummarizerConfig } from './client-summarizer.js';
|
|
3
|
+
export interface CollectorConfig {
|
|
4
|
+
apiUrl: string;
|
|
5
|
+
apiKey: string;
|
|
6
|
+
orgId: string;
|
|
7
|
+
projectId: string;
|
|
8
|
+
userId: string;
|
|
9
|
+
client: ClientType;
|
|
10
|
+
repoId?: string;
|
|
11
|
+
clientId?: string;
|
|
12
|
+
/** Workspace root path — if set, auto-writes .cursor/rules/ context */
|
|
13
|
+
workspacePath?: string;
|
|
14
|
+
/** Project name for cursor rules */
|
|
15
|
+
projectName?: string;
|
|
16
|
+
/** Dedup window in ms (default 30000). Set to 0 to disable. */
|
|
17
|
+
dedupWindowMs?: number;
|
|
18
|
+
/** Edge/native summarization config. When set, events are pre-summarized
|
|
19
|
+
* before reaching the API, saving server-side summarization cost. */
|
|
20
|
+
summarizer?: ClientSummarizerConfig;
|
|
21
|
+
}
|
|
22
|
+
/** Structured session summary matching claude-mem's summary schema */
|
|
23
|
+
export interface SessionSummary {
|
|
24
|
+
/** What the user requested */
|
|
25
|
+
request: string;
|
|
26
|
+
/** What was explored / investigated */
|
|
27
|
+
investigated?: string[];
|
|
28
|
+
/** Key learnings and discoveries */
|
|
29
|
+
learned?: string[];
|
|
30
|
+
/** What was delivered / completed */
|
|
31
|
+
completed?: string[];
|
|
32
|
+
/** Suggested follow-up work */
|
|
33
|
+
next_steps?: string[];
|
|
34
|
+
/** Additional notes */
|
|
35
|
+
notes?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Collector captures events from IDE environments and forwards them
|
|
39
|
+
* to the Memoire API. If the API is unavailable, events are queued
|
|
40
|
+
* locally and retried.
|
|
41
|
+
*
|
|
42
|
+
* Follows the claude-mem hook lifecycle pattern:
|
|
43
|
+
* SessionStart → PostToolUse → SessionEnd
|
|
44
|
+
*
|
|
45
|
+
* Edge processing:
|
|
46
|
+
* - Privacy: <private> tags are stripped before storage
|
|
47
|
+
* - Dedup: Content-hash deduplication within a 30s sliding window
|
|
48
|
+
* - Structured summaries: Session end accepts typed summary objects
|
|
49
|
+
*/
|
|
50
|
+
export declare class Collector {
|
|
51
|
+
private memoireClient;
|
|
52
|
+
private queue;
|
|
53
|
+
private config;
|
|
54
|
+
private sessionId;
|
|
55
|
+
private dedup;
|
|
56
|
+
private summarizer;
|
|
57
|
+
constructor(config: CollectorConfig);
|
|
58
|
+
/** Start a new session */
|
|
59
|
+
sessionStart(): Promise<void>;
|
|
60
|
+
/** Record an observation from a tool use */
|
|
61
|
+
observation(content: string, opts?: {
|
|
62
|
+
filesRead?: string[];
|
|
63
|
+
filesModified?: string[];
|
|
64
|
+
concepts?: string[];
|
|
65
|
+
branchName?: string;
|
|
66
|
+
}): Promise<void>;
|
|
67
|
+
/** Record a user prompt */
|
|
68
|
+
prompt(content: string, opts?: {
|
|
69
|
+
filesRead?: string[];
|
|
70
|
+
filesModified?: string[];
|
|
71
|
+
concepts?: string[];
|
|
72
|
+
branchName?: string;
|
|
73
|
+
}): Promise<void>;
|
|
74
|
+
/** Record a decision */
|
|
75
|
+
decision(content: string, opts?: {
|
|
76
|
+
filesModified?: string[];
|
|
77
|
+
concepts?: string[];
|
|
78
|
+
branchName?: string;
|
|
79
|
+
}): Promise<void>;
|
|
80
|
+
/** Record a failed attempt */
|
|
81
|
+
attempt(content: string, opts?: {
|
|
82
|
+
filesModified?: string[];
|
|
83
|
+
concepts?: string[];
|
|
84
|
+
branchName?: string;
|
|
85
|
+
}): Promise<void>;
|
|
86
|
+
/** Record a convention or standard */
|
|
87
|
+
convention(content: string, opts?: {
|
|
88
|
+
filesModified?: string[];
|
|
89
|
+
concepts?: string[];
|
|
90
|
+
branchName?: string;
|
|
91
|
+
}): Promise<void>;
|
|
92
|
+
/** Record a branch lifecycle event */
|
|
93
|
+
branchEvent(content: string, opts?: {
|
|
94
|
+
filesModified?: string[];
|
|
95
|
+
concepts?: string[];
|
|
96
|
+
branchName?: string;
|
|
97
|
+
}): Promise<void>;
|
|
98
|
+
/** End a session with a structured or plain-text summary */
|
|
99
|
+
sessionEnd(summary: string | SessionSummary): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* Update .cursor/rules/memoire-context.mdc with latest project context.
|
|
102
|
+
* Called automatically on session end for Cursor clients.
|
|
103
|
+
*/
|
|
104
|
+
private updateCursorRules;
|
|
105
|
+
/** Emit a raw event — applies privacy stripping, dedup, and optional client-side summarization */
|
|
106
|
+
private emit;
|
|
107
|
+
/** Get the number of pending events in the offline queue */
|
|
108
|
+
get pendingEvents(): number;
|
|
109
|
+
destroy(): void;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../src/capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.integration.d.ts","sourceRoot":"","sources":["../src/capture.integration.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import postgres from 'postgres';
|
|
4
|
+
import { Collector } from './capture.js';
|
|
5
|
+
const integrationEnv = {
|
|
6
|
+
apiUrl: process.env.MEMOIRE_INTEGRATION_API_URL,
|
|
7
|
+
apiKey: process.env.MEMOIRE_INTEGRATION_API_KEY,
|
|
8
|
+
orgId: process.env.MEMOIRE_INTEGRATION_ORG_ID,
|
|
9
|
+
projectId: process.env.MEMOIRE_INTEGRATION_PROJECT_ID,
|
|
10
|
+
userId: process.env.MEMOIRE_INTEGRATION_USER_ID,
|
|
11
|
+
databaseUrl: process.env.MEMOIRE_INTEGRATION_DATABASE_URL,
|
|
12
|
+
};
|
|
13
|
+
const missingEnv = Object.entries(integrationEnv)
|
|
14
|
+
.filter(([, value]) => !value)
|
|
15
|
+
.map(([key]) => key);
|
|
16
|
+
test('collector forwards captured events to the API and persists them', { skip: missingEnv.length > 0 ? `missing env: ${missingEnv.join(', ')}` : false }, async () => {
|
|
17
|
+
const collector = new Collector({
|
|
18
|
+
apiUrl: integrationEnv.apiUrl,
|
|
19
|
+
apiKey: integrationEnv.apiKey,
|
|
20
|
+
orgId: integrationEnv.orgId,
|
|
21
|
+
projectId: integrationEnv.projectId,
|
|
22
|
+
userId: integrationEnv.userId,
|
|
23
|
+
client: 'cursor',
|
|
24
|
+
repoId: 'memoire/integration',
|
|
25
|
+
});
|
|
26
|
+
const sql = postgres(integrationEnv.databaseUrl);
|
|
27
|
+
const content = `collector integration ${Date.now()}`;
|
|
28
|
+
try {
|
|
29
|
+
await collector.observation(content, {
|
|
30
|
+
branchName: 'integration/collector',
|
|
31
|
+
filesModified: ['src/auth/config.ts'],
|
|
32
|
+
concepts: ['integration-test'],
|
|
33
|
+
});
|
|
34
|
+
const row = await waitForEvent(sql, {
|
|
35
|
+
content,
|
|
36
|
+
projectId: integrationEnv.projectId,
|
|
37
|
+
});
|
|
38
|
+
assert.ok(row, 'expected collector event to be persisted');
|
|
39
|
+
assert.equal(row.event_type, 'observation');
|
|
40
|
+
assert.equal(row.client, 'cursor');
|
|
41
|
+
assert.equal(row.project_id, integrationEnv.projectId);
|
|
42
|
+
assert.match(row.files_modified, /src\/auth\/config\.ts/);
|
|
43
|
+
await sql `delete from events where id = ${row.id}`;
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
collector.destroy();
|
|
47
|
+
await sql.end({ timeout: 5 });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
async function waitForEvent(sql, input) {
|
|
51
|
+
for (let attempt = 0; attempt < 20; attempt++) {
|
|
52
|
+
const rows = await sql `
|
|
53
|
+
select id, project_id, event_type, client, files_modified::text as files_modified
|
|
54
|
+
from events
|
|
55
|
+
where project_id = ${input.projectId}
|
|
56
|
+
and content = ${input.content}
|
|
57
|
+
order by inserted_at desc
|
|
58
|
+
limit 1
|
|
59
|
+
`;
|
|
60
|
+
if (rows[0]) {
|
|
61
|
+
return rows[0];
|
|
62
|
+
}
|
|
63
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=capture.integration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.integration.js","sourceRoot":"","sources":["../src/capture.integration.ts"],"names":[],"mappings":"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"}
|