chat-recall 0.0.1 → 0.4.8
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/LICENSE +93 -0
- package/dist/auto-update.d.ts +52 -0
- package/dist/auto-update.d.ts.map +1 -0
- package/dist/auto-update.js +157 -0
- package/dist/auto-update.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +26102 -0
- package/dist/cli.js.map +1 -0
- package/dist/client-events.d.ts +11 -0
- package/dist/client-events.d.ts.map +1 -0
- package/dist/client-events.js +77 -0
- package/dist/client-events.js.map +1 -0
- package/dist/collector-migrate.d.ts +27 -0
- package/dist/collector-migrate.d.ts.map +1 -0
- package/dist/collector-migrate.js +60 -0
- package/dist/collector-migrate.js.map +1 -0
- package/dist/device-auth.d.ts +37 -0
- package/dist/device-auth.d.ts.map +1 -0
- package/dist/device-auth.js +161 -0
- package/dist/device-auth.js.map +1 -0
- package/dist/escalate.d.ts +99 -0
- package/dist/escalate.d.ts.map +1 -0
- package/dist/escalate.js +219 -0
- package/dist/escalate.js.map +1 -0
- package/dist/http.d.ts +14 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +49 -0
- package/dist/http.js.map +1 -0
- package/dist/install-skills.d.ts +51 -0
- package/dist/install-skills.d.ts.map +1 -0
- package/dist/install-skills.js +223 -0
- package/dist/install-skills.js.map +1 -0
- package/dist/intent-drain.d.ts +25 -0
- package/dist/intent-drain.d.ts.map +1 -0
- package/dist/intent-drain.js +189 -0
- package/dist/intent-drain.js.map +1 -0
- package/dist/keyring.d.ts +28 -0
- package/dist/keyring.d.ts.map +1 -0
- package/dist/keyring.js +163 -0
- package/dist/keyring.js.map +1 -0
- package/dist/mcp.d.ts +8 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +23665 -0
- package/dist/mcp.js.map +1 -0
- package/dist/project-tasks.d.ts +54 -0
- package/dist/project-tasks.d.ts.map +1 -0
- package/dist/project-tasks.js +204 -0
- package/dist/project-tasks.js.map +1 -0
- package/dist/repair.d.ts +73 -0
- package/dist/repair.d.ts.map +1 -0
- package/dist/repair.js +267 -0
- package/dist/repair.js.map +1 -0
- package/dist/self-restart.d.ts +91 -0
- package/dist/self-restart.d.ts.map +1 -0
- package/dist/self-restart.js +137 -0
- package/dist/self-restart.js.map +1 -0
- package/dist/service-installer.d.ts +32 -0
- package/dist/service-installer.d.ts.map +1 -0
- package/dist/service-installer.js +231 -0
- package/dist/service-installer.js.map +1 -0
- package/dist/sync-client.d.ts +234 -0
- package/dist/sync-client.d.ts.map +1 -0
- package/dist/sync-client.js +1985 -0
- package/dist/sync-client.js.map +1 -0
- package/dist/sync-ledger.d.ts +131 -0
- package/dist/sync-ledger.d.ts.map +1 -0
- package/dist/sync-ledger.js +366 -0
- package/dist/sync-ledger.js.map +1 -0
- package/dist/update-notice.d.ts +16 -0
- package/dist/update-notice.d.ts.map +1 -0
- package/dist/update-notice.js +107 -0
- package/dist/update-notice.js.map +1 -0
- package/dist/verify-deep.d.ts +79 -0
- package/dist/verify-deep.d.ts.map +1 -0
- package/dist/verify-deep.js +97 -0
- package/dist/verify-deep.js.map +1 -0
- package/dist/verify-repair.d.ts +21 -0
- package/dist/verify-repair.d.ts.map +1 -0
- package/dist/verify-repair.js +58 -0
- package/dist/verify-repair.js.map +1 -0
- package/dist/watch.js +21550 -0
- package/hooks/chat_recall_escalate_hook.sh +51 -0
- package/hooks/chat_recall_resume_hook.sh +69 -0
- package/hooks/chat_recall_save_hook.sh +254 -0
- package/hooks/chat_recall_wakeup_hook.sh +39 -0
- package/package.json +38 -4
- package/skills/chat-recall/SKILL.md +76 -0
- package/skills/chat-recall-changes/SKILL.md +50 -0
- package/skills/chat-recall-find/SKILL.md +64 -0
- package/skills/chat-recall-memory/SKILL.md +55 -0
- package/skills/chat-recall-project/SKILL.md +75 -0
- package/skills/chat-recall-resume/SKILL.md +62 -0
- package/README.md +0 -7
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# chat-recall escalate hook — fires on SessionEnd.
|
|
3
|
+
#
|
|
4
|
+
# The write half of the Context Engineering loop: the SessionStart wake-up
|
|
5
|
+
# hook injects prior knowledge INTO a new session; this hook escalates the
|
|
6
|
+
# finished session's learnings (decisions the agent announced, corrections
|
|
7
|
+
# the user gave, and the outcome) OUT into the temporal knowledge graph via
|
|
8
|
+
# `chat-recall escalate`, so the next wake-up already knows them.
|
|
9
|
+
#
|
|
10
|
+
# Runs asynchronously: the work is backgrounded and detached, so session end
|
|
11
|
+
# never waits on network writes. The delay gives the collector a chance to
|
|
12
|
+
# sync the session's final events before extraction.
|
|
13
|
+
#
|
|
14
|
+
# Always exits 0. A hook must never break session end.
|
|
15
|
+
#
|
|
16
|
+
# Configurable via env:
|
|
17
|
+
# CHAT_RECALL_BIN — path to the chat-recall binary (default: which chat-recall)
|
|
18
|
+
# CHAT_RECALL_ESCALATE_TIMEOUT — seconds before the background run gives up (default 60)
|
|
19
|
+
# CHAT_RECALL_ESCALATE_DELAY — seconds to wait for the final sync (default 20)
|
|
20
|
+
|
|
21
|
+
set -uo pipefail
|
|
22
|
+
|
|
23
|
+
# Claude Code passes the hook payload as JSON on stdin; cwd is the project dir.
|
|
24
|
+
INPUT="$(cat 2>/dev/null || true)"
|
|
25
|
+
CWD=""
|
|
26
|
+
SESSION_ID=""
|
|
27
|
+
if command -v jq >/dev/null 2>&1; then
|
|
28
|
+
CWD="$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null || true)"
|
|
29
|
+
SESSION_ID="$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null || true)"
|
|
30
|
+
fi
|
|
31
|
+
[[ -z "$CWD" ]] && CWD="$PWD"
|
|
32
|
+
|
|
33
|
+
CHAT_RECALL_BIN="${CHAT_RECALL_BIN:-$(command -v chat-recall || true)}"
|
|
34
|
+
if [[ -z "$CHAT_RECALL_BIN" ]] || [[ ! -x "$CHAT_RECALL_BIN" ]]; then exit 0; fi
|
|
35
|
+
|
|
36
|
+
TIMEOUT="${CHAT_RECALL_ESCALATE_TIMEOUT:-60}"
|
|
37
|
+
DELAY="${CHAT_RECALL_ESCALATE_DELAY:-20}"
|
|
38
|
+
|
|
39
|
+
# Detach completely: no stdio ties back to the session, so Claude Code's
|
|
40
|
+
# hook runner returns immediately and the escalation happens in the background.
|
|
41
|
+
(
|
|
42
|
+
sleep "$DELAY"
|
|
43
|
+
cd "$CWD" 2>/dev/null || true
|
|
44
|
+
if [[ -n "$SESSION_ID" ]]; then
|
|
45
|
+
timeout "$TIMEOUT" "$CHAT_RECALL_BIN" escalate "$SESSION_ID" >/dev/null 2>&1 || true
|
|
46
|
+
else
|
|
47
|
+
timeout "$TIMEOUT" "$CHAT_RECALL_BIN" escalate --latest >/dev/null 2>&1 || true
|
|
48
|
+
fi
|
|
49
|
+
) </dev/null >/dev/null 2>&1 &
|
|
50
|
+
|
|
51
|
+
exit 0
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# chat-recall resume hint hook — fires on UserPromptSubmit.
|
|
3
|
+
#
|
|
4
|
+
# Reads the user's typed prompt from stdin (Claude Code hook input format) and,
|
|
5
|
+
# if it looks like a "real" task (not a slash command, not a one-word reply),
|
|
6
|
+
# searches chat-recall for sessions that worked on something similar. If high-
|
|
7
|
+
# confidence matches exist, prints a short note that gets injected into the
|
|
8
|
+
# session as additional context.
|
|
9
|
+
#
|
|
10
|
+
# Exit code 0 + stdout → injected as context for the assistant.
|
|
11
|
+
# Exit code 0 + no stdout → silent (the default when nothing useful to say).
|
|
12
|
+
# We never block the prompt (would be exit code 2). This is advisory only.
|
|
13
|
+
#
|
|
14
|
+
# Configurable via env:
|
|
15
|
+
# CHAT_RECALL_RESUME_THRESHOLD — min top score to surface (default 0.001 for FTS5)
|
|
16
|
+
# CHAT_RECALL_RESUME_MAX — max number of matches to mention (default 3)
|
|
17
|
+
# CHAT_RECALL_BIN — path to the chat-recall binary (default: which chat-recall)
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
# Read the hook payload from stdin. Tolerate non-JSON or missing fields.
|
|
22
|
+
INPUT="$(cat)"
|
|
23
|
+
|
|
24
|
+
# Extract prompt — bail out cleanly if jq is missing or input is malformed.
|
|
25
|
+
if ! command -v jq >/dev/null 2>&1; then exit 0; fi
|
|
26
|
+
PROMPT="$(echo "$INPUT" | jq -r '.prompt // empty')"
|
|
27
|
+
|
|
28
|
+
# Skip if there's nothing useful to search on:
|
|
29
|
+
# - empty / very short prompts
|
|
30
|
+
# - slash commands (start with /)
|
|
31
|
+
# - replies that are just confirmations ("yes", "ok", etc.)
|
|
32
|
+
if [[ -z "$PROMPT" ]]; then exit 0; fi
|
|
33
|
+
if [[ "${#PROMPT}" -lt 20 ]]; then exit 0; fi
|
|
34
|
+
if [[ "$PROMPT" == /* ]]; then exit 0; fi
|
|
35
|
+
if [[ "$PROMPT" =~ ^(yes|no|ok|okay|sure|continue|go|next|y|n)[[:space:]\.\!\?]*$ ]]; then exit 0; fi
|
|
36
|
+
|
|
37
|
+
CHAT_RECALL_BIN="${CHAT_RECALL_BIN:-$(command -v chat-recall || true)}"
|
|
38
|
+
if [[ -z "$CHAT_RECALL_BIN" ]] || [[ ! -x "$CHAT_RECALL_BIN" ]]; then
|
|
39
|
+
# chat-recall not on PATH — install it first; nothing to do.
|
|
40
|
+
exit 0
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
MAX="${CHAT_RECALL_RESUME_MAX:-3}"
|
|
44
|
+
|
|
45
|
+
# Search the index. We use the search subcommand (not MCP) because hooks need
|
|
46
|
+
# to be fast and synchronous. Output is plain text we'll grep for session ids.
|
|
47
|
+
# Cap the prompt at 400 chars to avoid blowing up FTS5 query parsing.
|
|
48
|
+
PROMPT_SHORT="${PROMPT:0:400}"
|
|
49
|
+
SEARCH_OUT="$("$CHAT_RECALL_BIN" search "$PROMPT_SHORT" --top "$MAX" 2>/dev/null || true)"
|
|
50
|
+
|
|
51
|
+
# If we got no results back, exit silently — no context to add.
|
|
52
|
+
if [[ -z "$SEARCH_OUT" ]] || ! echo "$SEARCH_OUT" | grep -qE 'session|score|project'; then
|
|
53
|
+
exit 0
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Emit a compact, agent-friendly hint. The leading marker lets the assistant
|
|
57
|
+
# recognize the source of this context.
|
|
58
|
+
{
|
|
59
|
+
echo "<!-- chat-recall-resume-hint -->"
|
|
60
|
+
echo "**📚 You've worked on something similar before** — chat-recall found these sessions:"
|
|
61
|
+
echo
|
|
62
|
+
# Take the first ~15 lines of search output as the digest. The CLI already
|
|
63
|
+
# formats nicely; we just trim.
|
|
64
|
+
echo "$SEARCH_OUT" | head -n 18
|
|
65
|
+
echo
|
|
66
|
+
echo "_Use \`recall_show <session-id>\` or \`recall_smart_resume <session-id>\` to load full context if relevant._"
|
|
67
|
+
} || true
|
|
68
|
+
|
|
69
|
+
exit 0
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# chat-recall save hook — auto-saves memory every 15 messages
|
|
3
|
+
# Installed as a Claude Code hook in ~/.claude/hooks.json
|
|
4
|
+
#
|
|
5
|
+
# This hook fires every 15 messages during a session.
|
|
6
|
+
# It extracts key facts (decisions, topics, tools) and saves them
|
|
7
|
+
# so they're preserved even if the session ends unexpectedly.
|
|
8
|
+
#
|
|
9
|
+
# Setup (one-time):
|
|
10
|
+
# 1. Copy this file to ~/.claude/chat-recall-hooks/
|
|
11
|
+
# 2. Add to ~/.claude/hooks.json:
|
|
12
|
+
# {
|
|
13
|
+
# "hooks": {
|
|
14
|
+
# "Stop": [{"matcher": "", "hooks": [{"type": "command", "command": "/home/YOU/.claude/chat-recall-hooks/chat_recall_save_hook.sh"}]}],
|
|
15
|
+
# "PreCompact": [{"matcher": "", "hooks": [{"type": "command", "command": "/home/YOU/.claude/chat-recall-hooks/chat_recall_save_hook.sh --precompact"}]}]
|
|
16
|
+
# }
|
|
17
|
+
# }
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
HOOKS_DIR="${HOME}/.claude/chat-recall-hooks"
|
|
22
|
+
MEMORY_DIR="${HOME}/.claude/chat-recall-memory"
|
|
23
|
+
STATE_FILE="${MEMORY_DIR}/last_save.json"
|
|
24
|
+
SESSION_ID=""
|
|
25
|
+
|
|
26
|
+
log() {
|
|
27
|
+
echo "[chat-recall-save] $(date +%H:%M:%S) $*" >&2
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
init() {
|
|
31
|
+
mkdir -p "${MEMORY_DIR}"
|
|
32
|
+
mkdir -p "${HOOKS_DIR}"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Detect current session from transcript
|
|
36
|
+
detect_session() {
|
|
37
|
+
local projects_dir="${HOME}/.claude/projects"
|
|
38
|
+
|
|
39
|
+
# Find the most recently modified session file
|
|
40
|
+
local latest_file=""
|
|
41
|
+
local latest_time=0
|
|
42
|
+
|
|
43
|
+
if [[ -d "$projects_dir" ]]; then
|
|
44
|
+
for dir in "$projects_dir"/*/; do
|
|
45
|
+
if [[ -d "$dir" ]]; then
|
|
46
|
+
for file in "$dir"/*.jsonl; do
|
|
47
|
+
if [[ -f "$file" ]]; then
|
|
48
|
+
local mtime
|
|
49
|
+
mtime=$(stat -c %Y "$file" 2>/dev/null || stat -f %m "$file" 2>/dev/null || echo 0)
|
|
50
|
+
if [[ "$mtime" -gt "$latest_time" ]]; then
|
|
51
|
+
latest_time="$mtime"
|
|
52
|
+
latest_file="$file"
|
|
53
|
+
fi
|
|
54
|
+
fi
|
|
55
|
+
done
|
|
56
|
+
fi
|
|
57
|
+
done
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
if [[ -n "$latest_file" && -f "$latest_file" ]]; then
|
|
61
|
+
SESSION_ID=$(basename "$latest_file" .jsonl)
|
|
62
|
+
fi
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# Extract key facts from a session file
|
|
66
|
+
extract_facts() {
|
|
67
|
+
local session_file="$1"
|
|
68
|
+
local session_id="$2"
|
|
69
|
+
|
|
70
|
+
if [[ ! -f "$session_file" ]]; then
|
|
71
|
+
echo "{}"
|
|
72
|
+
return
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
# Count messages
|
|
76
|
+
local msg_count
|
|
77
|
+
msg_count=$(wc -l < "$session_file" 2>/dev/null || echo 0)
|
|
78
|
+
|
|
79
|
+
# Extract unique tools used
|
|
80
|
+
local tools
|
|
81
|
+
tools=$(grep -o '"type":"tool_use"' "$session_file" 2>/dev/null | wc -l | tr -d ' ')
|
|
82
|
+
|
|
83
|
+
# Extract topics from first user message
|
|
84
|
+
local first_topic
|
|
85
|
+
first_topic=$(grep -m1 '"type":"user"' "$session_file" 2>/dev/null | \
|
|
86
|
+
sed 's/.*"text":"//' | sed 's/".*//' | head -c 200 | tr -d '\n' | tr -d '"')
|
|
87
|
+
|
|
88
|
+
# Extract decisions (heuristic)
|
|
89
|
+
local decisions=""
|
|
90
|
+
decisions=$(grep -i "decided\|chose\|going with\|recommend\|prefer" "$session_file" 2>/dev/null | \
|
|
91
|
+
head -3 | sed 's/.*"text":"//' | sed 's/".*//' | head -c 300 | tr -d '\n')
|
|
92
|
+
|
|
93
|
+
# Get project path
|
|
94
|
+
local project_path=""
|
|
95
|
+
for dir in "${HOME}"/.claude/projects/*/; do
|
|
96
|
+
if [[ -d "$dir" && -f "${dir}${session_id}.jsonl" ]]; then
|
|
97
|
+
project_path=$(echo "$dir" | sed "s|${HOME}/.claude/projects/||" | sed 's|/||g' | sed 's|^|/|')
|
|
98
|
+
project_path=$(echo "$project_path" | sed "s|-|/|g")
|
|
99
|
+
break
|
|
100
|
+
fi
|
|
101
|
+
done
|
|
102
|
+
|
|
103
|
+
# Output JSON
|
|
104
|
+
cat <<EOF
|
|
105
|
+
{
|
|
106
|
+
"session_id": "$session_id",
|
|
107
|
+
"project_path": "$project_path",
|
|
108
|
+
"message_count": $msg_count,
|
|
109
|
+
"tool_uses": $tools,
|
|
110
|
+
"first_topic": "$(echo "$first_topic" | jq -Rs '.' 2>/dev/null || echo "\"$first_topic\"")",
|
|
111
|
+
"decisions": "$(echo "$decisions" | jq -Rs '.' 2>/dev/null || echo "\"\"")",
|
|
112
|
+
"saved_at": "$(date -Iseconds)"
|
|
113
|
+
}
|
|
114
|
+
EOF
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Count messages in current session
|
|
118
|
+
count_messages() {
|
|
119
|
+
local session_file="$1"
|
|
120
|
+
if [[ ! -f "$session_file" ]]; then
|
|
121
|
+
echo 0
|
|
122
|
+
return
|
|
123
|
+
fi
|
|
124
|
+
grep -c '"type":"user"\|\|"type":"assistant"' "$session_file" 2>/dev/null || echo 0
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# Main save logic
|
|
128
|
+
do_save() {
|
|
129
|
+
local precompact="${1:-false}"
|
|
130
|
+
|
|
131
|
+
detect_session
|
|
132
|
+
|
|
133
|
+
if [[ -z "$SESSION_ID" ]]; then
|
|
134
|
+
log "No active session found"
|
|
135
|
+
return
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
# Find the session file
|
|
139
|
+
local session_file=""
|
|
140
|
+
for dir in "${HOME}"/.claude/projects/*/; do
|
|
141
|
+
if [[ -f "${dir}${SESSION_ID}.jsonl" ]]; then
|
|
142
|
+
session_file="${dir}${SESSION_ID}.jsonl"
|
|
143
|
+
break
|
|
144
|
+
fi
|
|
145
|
+
done
|
|
146
|
+
|
|
147
|
+
if [[ -z "$session_file" || ! -f "$session_file" ]]; then
|
|
148
|
+
log "Session file not found for $SESSION_ID"
|
|
149
|
+
return
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
# Extract and save facts
|
|
153
|
+
local facts
|
|
154
|
+
facts=$(extract_facts "$session_file" "$SESSION_ID")
|
|
155
|
+
|
|
156
|
+
local output_file="${MEMORY_DIR}/sessions/${SESSION_ID}.json"
|
|
157
|
+
mkdir -p "$(dirname "$output_file")"
|
|
158
|
+
|
|
159
|
+
# Merge with existing data if present
|
|
160
|
+
if [[ -f "$output_file" ]]; then
|
|
161
|
+
# Simple merge: update fields but preserve accumulated data
|
|
162
|
+
local existing_msg_count
|
|
163
|
+
existing_msg_count=$(jq -r '.accumulated_messages // 0' "$output_file" 2>/dev/null || echo 0)
|
|
164
|
+
local new_msg_count
|
|
165
|
+
new_msg_count=$(echo "$facts" | jq -r '.message_count')
|
|
166
|
+
local total_msgs=$((existing_msg_count + new_msg_count))
|
|
167
|
+
|
|
168
|
+
# Update but preserve accumulated decisions
|
|
169
|
+
local existing_decisions
|
|
170
|
+
existing_decisions=$(jq -r '.accumulated_decisions // []' "$output_file" 2>/dev/null || echo '[]')
|
|
171
|
+
|
|
172
|
+
echo "$facts" | jq --argjson decisions "$existing_decisions" --argjson total "$total_msgs" \
|
|
173
|
+
'. + {accumulated_messages: $total, accumulated_decisions: $decisions}' > "$output_file"
|
|
174
|
+
else
|
|
175
|
+
echo "$facts" | jq '. + {accumulated_messages: .message_count, accumulated_decisions: []}' > "$output_file"
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
# Track this save
|
|
179
|
+
echo "{\"session_id\": \"$SESSION_ID\", \"saved_at\": \"$(date -Iseconds)\", \"precompact\": $precompact}" > "${STATE_FILE}"
|
|
180
|
+
|
|
181
|
+
if [[ "$precompact" == "true" ]]; then
|
|
182
|
+
log "[PRECOMPACT] Emergency save for $SESSION_ID"
|
|
183
|
+
else
|
|
184
|
+
log "Saved facts for $SESSION_ID ($(echo "$facts" | jq -r '.message_count') messages)"
|
|
185
|
+
fi
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# Check if we should save (throttle to avoid too frequent saves)
|
|
189
|
+
should_save() {
|
|
190
|
+
local session_file="$1"
|
|
191
|
+
|
|
192
|
+
if [[ ! -f "${STATE_FILE}" ]]; then
|
|
193
|
+
return 0
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
# Check last save time
|
|
197
|
+
local last_save
|
|
198
|
+
last_save=$(jq -r '.saved_at // empty' "${STATE_FILE}" 2>/dev/null || echo "")
|
|
199
|
+
|
|
200
|
+
if [[ -z "$last_save" ]]; then
|
|
201
|
+
return 0
|
|
202
|
+
fi
|
|
203
|
+
|
|
204
|
+
# Don't save more than once per 30 seconds
|
|
205
|
+
local last_save_epoch
|
|
206
|
+
last_save_epoch=$(date -d "$last_save" +%s 2>/dev/null || echo 0)
|
|
207
|
+
local now_epoch
|
|
208
|
+
now_epoch=$(date +%s)
|
|
209
|
+
|
|
210
|
+
if (( now_epoch - last_save_epoch < 30 )); then
|
|
211
|
+
return 1
|
|
212
|
+
fi
|
|
213
|
+
|
|
214
|
+
return 0
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
main() {
|
|
218
|
+
init
|
|
219
|
+
|
|
220
|
+
local precompact="false"
|
|
221
|
+
if [[ "${1:-}" == "--precompact" ]]; then
|
|
222
|
+
precompact="true"
|
|
223
|
+
fi
|
|
224
|
+
|
|
225
|
+
detect_session
|
|
226
|
+
|
|
227
|
+
if [[ -z "$SESSION_ID" ]]; then
|
|
228
|
+
log "No session detected, skipping"
|
|
229
|
+
exit 0
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
# Find session file
|
|
233
|
+
local session_file=""
|
|
234
|
+
for dir in "${HOME}"/.claude/projects/*/; do
|
|
235
|
+
if [[ -f "${dir}${SESSION_ID}.jsonl" ]]; then
|
|
236
|
+
session_file="${dir}${SESSION_ID}.jsonl"
|
|
237
|
+
break
|
|
238
|
+
fi
|
|
239
|
+
done
|
|
240
|
+
|
|
241
|
+
if [[ -z "$session_file" ]]; then
|
|
242
|
+
log "Session file not found"
|
|
243
|
+
exit 1
|
|
244
|
+
fi
|
|
245
|
+
|
|
246
|
+
if ! should_save "$session_file"; then
|
|
247
|
+
log "Throttled (recent save)"
|
|
248
|
+
exit 0
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
do_save "$precompact"
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
main "$@"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# chat-recall wake-up hook — fires on SessionStart (matcher: startup|clear).
|
|
3
|
+
#
|
|
4
|
+
# Prints the project-scoped wake-up bundle (high-importance facts + current
|
|
5
|
+
# knowledge-graph facts) to stdout. Claude Code injects stdout into the new
|
|
6
|
+
# session's context. This gives manual sessions the same preamble that
|
|
7
|
+
# coolcode injects into its spawned sessions.
|
|
8
|
+
#
|
|
9
|
+
# Exit code 0 + stdout → injected as context.
|
|
10
|
+
# Any failure → silent exit 0. This hook is advisory and never blocks a session.
|
|
11
|
+
#
|
|
12
|
+
# Configurable via env:
|
|
13
|
+
# CHAT_RECALL_BIN — path to the chat-recall binary (default: which chat-recall)
|
|
14
|
+
# CHAT_RECALL_WAKEUP_TIMEOUT — seconds before we give up (default 10)
|
|
15
|
+
|
|
16
|
+
set -uo pipefail
|
|
17
|
+
|
|
18
|
+
if ! command -v jq >/dev/null 2>&1; then exit 0; fi
|
|
19
|
+
|
|
20
|
+
# Claude Code passes the hook payload as JSON on stdin; cwd is the project dir.
|
|
21
|
+
INPUT="$(cat 2>/dev/null || true)"
|
|
22
|
+
CWD="$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null || true)"
|
|
23
|
+
[[ -z "$CWD" ]] && CWD="$PWD"
|
|
24
|
+
PROJECT="$(basename "$CWD")"
|
|
25
|
+
|
|
26
|
+
CHAT_RECALL_BIN="${CHAT_RECALL_BIN:-$(command -v chat-recall || true)}"
|
|
27
|
+
if [[ -z "$CHAT_RECALL_BIN" ]] || [[ ! -x "$CHAT_RECALL_BIN" ]]; then exit 0; fi
|
|
28
|
+
|
|
29
|
+
TIMEOUT="${CHAT_RECALL_WAKEUP_TIMEOUT:-10}"
|
|
30
|
+
OUT="$(timeout "$TIMEOUT" "$CHAT_RECALL_BIN" memory wake-up -p "$PROJECT" 2>/dev/null || true)"
|
|
31
|
+
[[ -z "$OUT" ]] && exit 0
|
|
32
|
+
|
|
33
|
+
{
|
|
34
|
+
echo "<!-- chat-recall-wake-up -->"
|
|
35
|
+
echo "--- Prior knowledge (chat-recall wake-up, project: $PROJECT) ---"
|
|
36
|
+
echo "$OUT"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
exit 0
|
package/package.json
CHANGED
|
@@ -1,7 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chat-recall",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
"
|
|
3
|
+
"version": "0.4.8",
|
|
4
|
+
"description": "Local-first memory for AI coding tools \u2014 indexes Claude Code / Gemini CLI / OpenCode / Codex sessions, exposes them over MCP, optionally syncs redacted history to a chat-recall server",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Elastic-2.0",
|
|
7
|
+
"bin": {
|
|
8
|
+
"chat-recall": "./dist/cli.js",
|
|
9
|
+
"chat-recall-mcp": "./dist/mcp.js",
|
|
10
|
+
"chat-recall-watch": "./dist/watch.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"hooks",
|
|
15
|
+
"skills",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -b && node scripts/bundle.mjs && node scripts/postbuild.mjs",
|
|
21
|
+
"build:types": "tsc -b",
|
|
22
|
+
"auto-indexer": "tsx auto-indexer/indexer.ts"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
26
|
+
"@noble/ciphers": "^2.2.0",
|
|
27
|
+
"@noble/hashes": "^2.2.0",
|
|
28
|
+
"chalk": "^5.3.0",
|
|
29
|
+
"chokidar": "^3.5.3",
|
|
30
|
+
"commander": "^12.0.0",
|
|
31
|
+
"diff": "^9.0.0",
|
|
32
|
+
"dotenv": "^17.2.3",
|
|
33
|
+
"zod": "^3.22.0"
|
|
34
|
+
},
|
|
35
|
+
"optionalDependencies": {
|
|
36
|
+
"better-sqlite3": "12.9.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@chat-recall/engine": "*"
|
|
40
|
+
}
|
|
7
41
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: chat-recall
|
|
3
|
+
description: >-
|
|
4
|
+
Recall past AI-coding work — prior sessions, decisions, and project history —
|
|
5
|
+
through the chat-recall MCP tools (mcp__chat-recall__recall_*). Use when the
|
|
6
|
+
user says "continue" / "pick up where we left off", "did we decide X",
|
|
7
|
+
"remember when we…", "what was I doing", "what do you know about this
|
|
8
|
+
project/me", or otherwise references earlier work that is not in the current
|
|
9
|
+
context. Also the entry point for the surfaces no focused skill owns: leaked
|
|
10
|
+
secrets ("did I paste an API key"), the shared task board and team activity
|
|
11
|
+
("what are my open tasks", "what is my team working on"), and index health
|
|
12
|
+
("is chat-recall working", "re-index my sessions"). This is the hub: it routes
|
|
13
|
+
to the focused chat-recall skills and lists every recall_* tool. Reach for it
|
|
14
|
+
whenever the answer lives in PAST work rather than the files in front of you.
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# chat-recall — cross-session memory for AI coding
|
|
18
|
+
|
|
19
|
+
chat-recall indexes your Claude Code, Gemini, OpenCode, Codex and Antigravity
|
|
20
|
+
sessions — plus plans, tasks, CLAUDE.md files, shell history and agent diaries —
|
|
21
|
+
into a searchable server with a temporal knowledge graph. When a request depends
|
|
22
|
+
on something that happened in an *earlier* session (a decision, a past fix, "what
|
|
23
|
+
were we doing"), don't guess or ask the user to re-explain — recall it.
|
|
24
|
+
|
|
25
|
+
All tools are MCP tools on the `chat-recall` server, named
|
|
26
|
+
`mcp__chat-recall__<tool>`.
|
|
27
|
+
|
|
28
|
+
**Reads are safe to call proactively. Writes are not.** Everything below is a
|
|
29
|
+
read except these, which change stored memory or state — treat each as an edit
|
|
30
|
+
and apply the test in `chat-recall-memory` before calling one:
|
|
31
|
+
`recall_kg_add`, `recall_kg_invalidate`, `recall_decision_record`,
|
|
32
|
+
`recall_diary_write`, `recall_set`, `recall_task_create`, `recall_task_update`,
|
|
33
|
+
`recall_security_dismiss`, `recall_rename_session`, `recall_regenerate_summary`,
|
|
34
|
+
`recall_reclassify`, `recall_index`, `recall_code_index`.
|
|
35
|
+
|
|
36
|
+
## Route to the right skill
|
|
37
|
+
|
|
38
|
+
Match the intent, then read that skill and follow its workflow:
|
|
39
|
+
|
|
40
|
+
| The user wants… | Read skill |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| Continue / resume / pick up prior work; cold start on an ongoing task | `chat-recall-resume` |
|
|
43
|
+
| Find past conversations — "remember when", "have we done X", "find my notes on…" | `chat-recall-find` |
|
|
44
|
+
| Store or recall a decision / fact; "what do you know about…", "did we decide…" | `chat-recall-memory` |
|
|
45
|
+
| The state of a project — "what's been happening in X", cost, findings | `chat-recall-project` |
|
|
46
|
+
| What changed — edits, diffs, commits; "what was I doing 2h ago" | `chat-recall-changes` |
|
|
47
|
+
|
|
48
|
+
Leaked secrets, the shared task board, team activity and index health have no
|
|
49
|
+
focused skill — use the catalog below directly.
|
|
50
|
+
|
|
51
|
+
## Tool catalog
|
|
52
|
+
|
|
53
|
+
Every `recall_*` tool. `packages/cli/src/skills-catalog.test.ts` fails when a
|
|
54
|
+
tool is registered and never named here, so adding a tool means placing it.
|
|
55
|
+
|
|
56
|
+
**Resume / cold start** — `recall_smart_resume` (structured resume bundle; needs a session id), `recall_recent` (list recent sessions), `recall_wake_up` (identity + high-signal facts), `recall_context` (structured dump of one session), `recall_show` (raw slice — returns 10 messages unless you raise `max_messages`), `recall_summary` (AI summary + outcome).
|
|
57
|
+
**Search** — `recall_search` (sessions; `include_outcome`, `like_session`), `recall_memory_search` (every memory type), `recall_memory_item` (read ONE item found by search, or browse a source type), `recall_user_prompts` (what the user actually typed), `recall_subagent_search` (inside subagent transcripts, whose work never reaches the main conversation), `recall_redundant_files` (before writing a new file, check you have not written one like it already).
|
|
58
|
+
**Project** — `recall_project_context` (rich dump), `recall_weekly_digest`, `recall_analytics_summary`, `recall_outcome_summary` (how many recent sessions actually shipped), `recall_code_findings` / `recall_code_actions` / `recall_code_projects` / `recall_code_index` / `recall_recommendations`.
|
|
59
|
+
**What changed** — `recall_edits_timeline` (cross-tool edits), `recall_diff` (per-session diffs), `recall_commits` (did it actually land), `recall_markers` (where a session went sideways).
|
|
60
|
+
**Durable memory / KG** — `recall_kg_query` / `recall_kg_add` / `recall_kg_invalidate` / `recall_kg_timeline` / `recall_kg_stats`, `recall_decision_record`, `recall_diary_write` / `recall_diary_read`, `recall_set` / `recall_get`.
|
|
61
|
+
**Team** — `recall_tasks` (the shared task board), `recall_task_create`, `recall_task_update` (status, assignee, comment; task ids look like `t_…`), `recall_team_activity` (per-teammate × per-project rollup), `recall_shares` (which projects are shared — private by default, nothing is visible to teammates until shared).
|
|
62
|
+
**Security** — `recall_security_summary` (leaked secrets that still need action — start here for "did I paste a key somewhere"), `recall_security_session` (findings for one session), `recall_security_dismiss` (mark rotated / false positive — do this only when the user confirms which), `recall_security_rules` (tenant detection rules; also tests a regex).
|
|
63
|
+
**Health / maintenance** — `recall_status` (is the index alive, what is synced), `recall_index` (sync now), `recall_heal_audit` (sessions whose rendered text is thinner than the raw archive, i.e. truncated upstream), `recall_regenerate_summary` (stored summary is stale or wrong), `recall_reclassify` (re-run the classifier over old chunks), `recall_rename_session`.
|
|
64
|
+
|
|
65
|
+
## If you're unsure where to start
|
|
66
|
+
|
|
67
|
+
- Free-text "is there past work relevant to this?" → `recall_search` with `include_outcome: true`.
|
|
68
|
+
- Broader ("plans, tasks, CLAUDE.md, diaries too") → `recall_memory_search`.
|
|
69
|
+
- "What do you know about me / this project right now?" → `recall_wake_up` (add a project filter) or `recall_project_context`.
|
|
70
|
+
- "Continue where we left off" → you need a session id first. `recall_smart_resume`
|
|
71
|
+
takes one and REQUIRES it, so get the id from `recall_recent` (most recent work)
|
|
72
|
+
or `recall_search` (`include_outcome: true`, when the user described the task),
|
|
73
|
+
then resume. See `chat-recall-resume`.
|
|
74
|
+
|
|
75
|
+
Prefer one targeted recall over asking the user to recap. Cite what you found
|
|
76
|
+
(session id / decision) so the user can verify.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: chat-recall-changes
|
|
3
|
+
description: >-
|
|
4
|
+
See what changed across past AI-coding sessions with chat-recall. Use when the
|
|
5
|
+
user asks "what did I change 2h ago", "what files did we touch", "what happened
|
|
6
|
+
in that session", "did that session actually ship", "which sessions edited
|
|
7
|
+
<file>", or wants diffs/commits/edit-history that isn't in the current working
|
|
8
|
+
tree. Reconstructs edits, diffs, commits, and outcomes from indexed sessions.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# What-changed with chat-recall
|
|
12
|
+
|
|
13
|
+
## Recent edits across everything
|
|
14
|
+
|
|
15
|
+
`mcp__chat-recall__recall_edits_timeline` — chronological file edits across
|
|
16
|
+
every AI tool chat-recall indexes, read live from each tool's own session store.
|
|
17
|
+
`since_hours: 2` for "what was I doing recently"; `pattern` to filter by path;
|
|
18
|
+
`group_by: "session"` (+ wide `since_hours`) to answer "which sessions edited
|
|
19
|
+
<file> this month".
|
|
20
|
+
|
|
21
|
+
## One session's changes
|
|
22
|
+
|
|
23
|
+
Every tool here takes a NAMED `session_id` (not a positional argument).
|
|
24
|
+
|
|
25
|
+
- `mcp__chat-recall__recall_diff` — per-file unified diffs of what a session
|
|
26
|
+
actually changed (detects reverts). `files_only: true` = just the touched-files
|
|
27
|
+
list; `context_only: true` = per-file add/remove stats.
|
|
28
|
+
- `mcp__chat-recall__recall_commits` — git commits that landed in the
|
|
29
|
+
session's edit window, grouped by repo. Use to verify "shipped" claims: many
|
|
30
|
+
edits but no commit = work stayed local.
|
|
31
|
+
- `mcp__chat-recall__recall_summary` — AI summary + outcome classification
|
|
32
|
+
(shipped / interrupted / abandoned) + last-claim-vs-user-reaction.
|
|
33
|
+
- `mcp__chat-recall__recall_markers` — per-prompt sentiment/correction
|
|
34
|
+
markers (frustrated, correction, approval) to spot where a session went sideways.
|
|
35
|
+
|
|
36
|
+
## Across many sessions
|
|
37
|
+
|
|
38
|
+
`mcp__chat-recall__recall_outcome_summary` — how sessions ended over the last N
|
|
39
|
+
days (shipped / interrupted / abandoned / in_progress). Use for "am I finishing
|
|
40
|
+
what I start", never for one session — `recall_summary` answers that.
|
|
41
|
+
|
|
42
|
+
## Workflow
|
|
43
|
+
|
|
44
|
+
1. "What was I doing recently" → `recall_edits_timeline` (`since_hours`).
|
|
45
|
+
2. "Which sessions touched <file>" → `recall_edits_timeline`
|
|
46
|
+
(`pattern`, `group_by: "session"`, wide window).
|
|
47
|
+
3. "What did session X change / did it ship" → `recall_diff` + `recall_commits`
|
|
48
|
+
(+ `recall_summary` for the outcome).
|
|
49
|
+
4. Don't claim a session shipped without `recall_commits` confirming a matching
|
|
50
|
+
commit — edits alone can be local-only.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: chat-recall-find
|
|
3
|
+
description: >-
|
|
4
|
+
Find past AI-coding work with chat-recall. Use when the user asks "remember
|
|
5
|
+
when we…", "have we done X before", "find my notes/sessions on…", "where did we
|
|
6
|
+
discuss Y", "what did I ask about Z", or otherwise wants to locate earlier
|
|
7
|
+
conversations, plans, tasks, or notes across all their AI tools. Searches the
|
|
8
|
+
unified memory (sessions, plans, tasks, CLAUDE.md, history, diaries).
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Finding past work with chat-recall
|
|
12
|
+
|
|
13
|
+
## Pick the search tool
|
|
14
|
+
|
|
15
|
+
- `mcp__chat-recall__recall_search` — search SESSIONS. Free-text query.
|
|
16
|
+
- `include_outcome: true` → each hit gets a shipped/interrupted/abandoned line
|
|
17
|
+
so you can judge relevance fast.
|
|
18
|
+
- `like_session: <id>` → find sessions similar to a given one.
|
|
19
|
+
- `project_filter`, `top_k` to narrow.
|
|
20
|
+
- `mcp__chat-recall__recall_memory_search` — search ALL memory types, not just
|
|
21
|
+
sessions. Use this when the answer might be in a plan, task list, CLAUDE.md, or
|
|
22
|
+
an agent diary — not only a conversation. Omit `source_types` to search
|
|
23
|
+
everything; pass it to narrow. The conversation-ish types are
|
|
24
|
+
`['session','plan','task','claude_md','paste','history','diary','agent_memory']`,
|
|
25
|
+
and the toolkit types — what is CONFIGURED rather than what was said — are
|
|
26
|
+
`['skill','mcp','command','agent','hook','plugin']`.
|
|
27
|
+
- `mcp__chat-recall__recall_user_prompts` — search only what the USER actually
|
|
28
|
+
typed (assistant output stripped). Best for "what did I ask about X",
|
|
29
|
+
"what was I requesting yesterday".
|
|
30
|
+
- `mcp__chat-recall__recall_subagent_search` — search inside SUBAGENT
|
|
31
|
+
transcripts. Explore/compact/aside sub-tasks run within a session and their
|
|
32
|
+
work never appears in the main conversation, so a normal session search cannot
|
|
33
|
+
find it. Reach for this when a session clearly did work you cannot locate.
|
|
34
|
+
- `mcp__chat-recall__recall_redundant_files` — before creating a file, check
|
|
35
|
+
whether you have written one like it before. Scores a filename against every
|
|
36
|
+
file in the synced history.
|
|
37
|
+
|
|
38
|
+
## From a hit to the content
|
|
39
|
+
|
|
40
|
+
Every tool here takes a NAMED `session_id` (not a positional argument).
|
|
41
|
+
|
|
42
|
+
- `mcp__chat-recall__recall_show` — read the session slice (`around_line`,
|
|
43
|
+
`from_end`, `include_code: true` for diffs/commands). It returns **10 messages
|
|
44
|
+
by default**: raise `max_messages` before telling the user what a session
|
|
45
|
+
contains, or you will describe a tenth of it as the whole.
|
|
46
|
+
- `mcp__chat-recall__recall_summary` — AI summary + outcome.
|
|
47
|
+
- `mcp__chat-recall__recall_memory_item` — read ONE item in full (or its links)
|
|
48
|
+
once search has found it. `mode=browse` lists a whole source type.
|
|
49
|
+
- Plans/tasks: `recall_memory_search(source_types: ['plan','task'])` to find,
|
|
50
|
+
then `recall_show` with the plan id to read the full plan.
|
|
51
|
+
|
|
52
|
+
## Strategy
|
|
53
|
+
|
|
54
|
+
1. Sessions-only question → `recall_search` (add `include_outcome: true`).
|
|
55
|
+
2. Might be a plan/task/note/diary → `recall_memory_search` (set `source_types`).
|
|
56
|
+
3. "What did *I* ask" → `recall_user_prompts`.
|
|
57
|
+
4. Zero results → broaden the query by dropping qualifiers, drop or widen
|
|
58
|
+
`project_filter`, or try the other search tool. Neither search tool has a
|
|
59
|
+
date/time filter, so do NOT try to narrow by time here — for a time window
|
|
60
|
+
use `recall_recent` (`since_hours`) or `recall_edits_timeline` instead.
|
|
61
|
+
Then open the best hit with `recall_show`.
|
|
62
|
+
5. Work you cannot find in the session itself → `recall_subagent_search`.
|
|
63
|
+
|
|
64
|
+
Report the session id / source of each result so the user can jump to it.
|