mirror of
https://github.com/lukaszraczylo/claude-mnemonic.git
synced 2026-10-13 04:59:05 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b396d05c0f | ||
|
|
66c53deb02 |
+19
-2
@@ -75,13 +75,16 @@ func parseTranscript(path string) (lastUser, lastAssistant string) {
|
||||
continue
|
||||
}
|
||||
|
||||
if msg.Type == "message" {
|
||||
// Transcript entries have type: "user" or "assistant" (not "message")
|
||||
// Check if this is a user/assistant message with content
|
||||
if msg.Type == "user" || msg.Type == "assistant" {
|
||||
text := extractTextContent(msg.Message.Content)
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
switch msg.Message.Role {
|
||||
// Use the outer type field for role (message.role may differ)
|
||||
switch msg.Type {
|
||||
case "user":
|
||||
lastUser = text
|
||||
case "assistant":
|
||||
@@ -98,6 +101,9 @@ func main() {
|
||||
}
|
||||
|
||||
func handleStop(ctx *hooks.HookContext, input *Input) (string, error) {
|
||||
// Debug: dump raw input
|
||||
fmt.Fprintf(os.Stderr, "[stop] Raw input: %s\n", string(ctx.RawInput))
|
||||
|
||||
// Find session
|
||||
result, err := hooks.GET(ctx.Port, fmt.Sprintf("/api/sessions?claudeSessionId=%s", ctx.SessionID))
|
||||
if err != nil || result == nil {
|
||||
@@ -116,6 +122,17 @@ func handleStop(ctx *hooks.HookContext, input *Input) (string, error) {
|
||||
lastUser, lastAssistant = parseTranscript(input.TranscriptPath)
|
||||
}
|
||||
|
||||
// Debug: log what we extracted
|
||||
fmt.Fprintf(os.Stderr, "[stop] Transcript path: %s\n", input.TranscriptPath)
|
||||
fmt.Fprintf(os.Stderr, "[stop] Last user message length: %d\n", len(lastUser))
|
||||
fmt.Fprintf(os.Stderr, "[stop] Last assistant message length: %d\n", len(lastAssistant))
|
||||
if len(lastAssistant) > 0 {
|
||||
preview := lastAssistant
|
||||
if len(preview) > 300 {
|
||||
preview = preview[:300] + "..."
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "[stop] Last assistant preview: %s\n", preview)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "[stop] Requesting summary for session %d (transcript: %v)\n", int64(sessionID), input.TranscriptPath != "")
|
||||
|
||||
// Request summary with message context from transcript
|
||||
|
||||
@@ -175,6 +175,29 @@ func (s *ObservationStore) GetRecentObservations(ctx context.Context, project st
|
||||
return scanObservationRows(rows)
|
||||
}
|
||||
|
||||
// GetObservationsByProjectStrict retrieves observations strictly for a specific project.
|
||||
// Unlike GetRecentObservations, this does NOT include global observations from other projects.
|
||||
// Use this for dashboard filtering where the user expects to see only that project's data.
|
||||
func (s *ObservationStore) GetObservationsByProjectStrict(ctx context.Context, project string, limit int) ([]*models.Observation, error) {
|
||||
const query = `
|
||||
SELECT id, sdk_session_id, project, COALESCE(scope, 'project') as scope, type, title, subtitle, facts, narrative,
|
||||
concepts, files_read, files_modified, file_mtimes, prompt_number, discovery_tokens,
|
||||
created_at, created_at_epoch
|
||||
FROM observations
|
||||
WHERE project = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT ?
|
||||
`
|
||||
|
||||
rows, err := s.store.QueryContext(ctx, query, project, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
return scanObservationRows(rows)
|
||||
}
|
||||
|
||||
// GetObservationCount returns the count of observations for a project (including global).
|
||||
func (s *ObservationStore) GetObservationCount(ctx context.Context, project string) (int, error) {
|
||||
const query = `
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1.23.2
|
||||
@@ -0,0 +1 @@
|
||||
1.23.2
|
||||
@@ -0,0 +1 @@
|
||||
1.23.2
|
||||
@@ -0,0 +1 @@
|
||||
1.23.2
|
||||
@@ -478,8 +478,8 @@ func (s *Service) handleGetObservations(w http.ResponseWriter, r *http.Request)
|
||||
// Fall back to SQLite if vector search not used
|
||||
if !usedVector {
|
||||
if project != "" {
|
||||
// Filter by project - includes project-scoped and global observations
|
||||
observations, err = s.observationStore.GetRecentObservations(r.Context(), project, limit)
|
||||
// Strict project filtering for dashboard - only observations from this project
|
||||
observations, err = s.observationStore.GetObservationsByProjectStrict(r.Context(), project, limit)
|
||||
} else {
|
||||
// All projects
|
||||
observations, err = s.observationStore.GetAllRecentObservations(r.Context(), limit)
|
||||
|
||||
@@ -234,11 +234,19 @@ func (p *Processor) ProcessObservation(ctx context.Context, sdkSessionID, projec
|
||||
|
||||
// ProcessSummary processes a session summary request.
|
||||
func (p *Processor) ProcessSummary(ctx context.Context, sessionDBID int64, sdkSessionID, project, userPrompt, lastUserMsg, lastAssistantMsg string) error {
|
||||
// Debug: log what we received
|
||||
log.Debug().
|
||||
Int64("sessionId", sessionDBID).
|
||||
Int("lastAssistantMsgLen", len(lastAssistantMsg)).
|
||||
Str("lastAssistantMsgPreview", truncateForLog(lastAssistantMsg, 200)).
|
||||
Msg("ProcessSummary called")
|
||||
|
||||
// Skip summary generation if there's no meaningful assistant response
|
||||
// This prevents generic "initial session setup" summaries
|
||||
if !hasMeaningfulContent(lastAssistantMsg) {
|
||||
log.Info().
|
||||
Int64("sessionId", sessionDBID).
|
||||
Int("msgLen", len(lastAssistantMsg)).
|
||||
Msg("Skipping summary - no meaningful assistant response")
|
||||
return nil
|
||||
}
|
||||
@@ -727,6 +735,14 @@ func hasMeaningfulContent(assistantMsg string) bool {
|
||||
return matchCount >= 2
|
||||
}
|
||||
|
||||
// truncateForLog truncates a string for logging purposes.
|
||||
func truncateForLog(s string, maxLen int) string {
|
||||
if len(s) <= maxLen {
|
||||
return s
|
||||
}
|
||||
return s[:maxLen] + "..."
|
||||
}
|
||||
|
||||
const systemPrompt = `You are a memory extraction agent for Claude Code sessions. Your job is to analyze tool executions and extract meaningful observations that would be useful for future sessions.
|
||||
|
||||
GUIDELINES:
|
||||
|
||||
+9
-9
@@ -13,7 +13,7 @@
|
||||
{
|
||||
"name": "claude-mnemonic",
|
||||
"description": "Persistent memory system for Claude Code - Go implementation with SQLite and ChromaDB vector search",
|
||||
"version": "0.6.1",
|
||||
"version": "0.6.4",
|
||||
"author": {
|
||||
"name": "lukaszraczylo",
|
||||
"url": "https://github.com/lukaszraczylo"
|
||||
@@ -23,25 +23,25 @@
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/lukaszraczylo/claude-mnemonic",
|
||||
"releases": {
|
||||
"latest": "0.6.1",
|
||||
"latest": "0.6.4",
|
||||
"versions": {
|
||||
"0.6.1": {
|
||||
"0.6.4": {
|
||||
"releaseDate": "2025-12-16",
|
||||
"downloads": {
|
||||
"darwin-arm64": {
|
||||
"url": "https://github.com/lukaszraczylo/claude-mnemonic/releases/download/v0.6.1/claude-mnemonic_0.6.1_darwin_arm64.tar.gz",
|
||||
"url": "https://github.com/lukaszraczylo/claude-mnemonic/releases/download/v0.6.4/claude-mnemonic_0.6.4_darwin_arm64.tar.gz",
|
||||
"format": "tar.gz",
|
||||
"sha256": "b603f2d7721a5186ee2c5cc410f6e4180e8dc6167a5cb6dc835a4755c449197b"
|
||||
"sha256": "5bb082901e8b4f4751d011675a235a5ed25403204732e09842b5ae75136f65ae"
|
||||
},
|
||||
"linux-amd64": {
|
||||
"url": "https://github.com/lukaszraczylo/claude-mnemonic/releases/download/v0.6.1/claude-mnemonic_0.6.1_linux_amd64.tar.gz",
|
||||
"url": "https://github.com/lukaszraczylo/claude-mnemonic/releases/download/v0.6.4/claude-mnemonic_0.6.4_linux_amd64.tar.gz",
|
||||
"format": "tar.gz",
|
||||
"sha256": "ccd79a9ac29f65f0177b4279855c10a20743856efbae4af41e7343662dc290b5"
|
||||
"sha256": "dee774fbc39e78ee6ff3836403c2901471dd0d417f62e214088f6b8285b3b23d"
|
||||
},
|
||||
"windows-amd64": {
|
||||
"url": "https://github.com/lukaszraczylo/claude-mnemonic/releases/download/v0.6.1/claude-mnemonic_0.6.1_windows_amd64.zip",
|
||||
"url": "https://github.com/lukaszraczylo/claude-mnemonic/releases/download/v0.6.4/claude-mnemonic_0.6.4_windows_amd64.zip",
|
||||
"format": "zip",
|
||||
"sha256": "f58734356be2b22f32d75d6ca8025a359e2cbe86edcc78c6586b26a571471e62"
|
||||
"sha256": "e3973bc1107f9cc92cf95697c62138b4cab4d0d4e00f836dfc4592ca84bbdab7"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
#!/bin/bash
|
||||
# Download ONNX Runtime libraries for embedding
|
||||
# Usage: ./download-onnx-libs.sh [platform]
|
||||
# Usage: ./download-onnx-libs.sh [platform] [--force]
|
||||
# Platform: darwin-arm64, linux-amd64, windows-amd64, or "all" (default)
|
||||
# Use --force to re-download even if libraries exist
|
||||
|
||||
set -e
|
||||
|
||||
ONNX_VERSION="1.23.2"
|
||||
ASSETS_DIR="internal/embedding/assets/lib"
|
||||
PLATFORM="${1:-all}"
|
||||
FORCE_DOWNLOAD=false
|
||||
|
||||
# Check for --force flag
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" = "--force" ]; then
|
||||
FORCE_DOWNLOAD=true
|
||||
fi
|
||||
done
|
||||
|
||||
# Auto-detect platform if not specified
|
||||
if [ "$PLATFORM" = "auto" ]; then
|
||||
@@ -31,12 +40,38 @@ fi
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
trap "rm -rf ${TEMP_DIR}" EXIT
|
||||
|
||||
# Get the installed version for a platform
|
||||
get_installed_version() {
|
||||
local plat="$1"
|
||||
local version_file="${ASSETS_DIR}/${plat}/.version"
|
||||
if [ -f "$version_file" ]; then
|
||||
cat "$version_file"
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# Write version file after successful download
|
||||
write_version_file() {
|
||||
local plat="$1"
|
||||
echo "${ONNX_VERSION}" > "${ASSETS_DIR}/${plat}/.version"
|
||||
}
|
||||
|
||||
# Check if version matches
|
||||
version_matches() {
|
||||
local plat="$1"
|
||||
local installed_version
|
||||
installed_version=$(get_installed_version "$plat")
|
||||
[ "$installed_version" = "$ONNX_VERSION" ]
|
||||
}
|
||||
|
||||
download_darwin_arm64() {
|
||||
echo "Downloading darwin-arm64..."
|
||||
mkdir -p "${ASSETS_DIR}/darwin-arm64"
|
||||
curl -fsSL "https://github.com/microsoft/onnxruntime/releases/download/v${ONNX_VERSION}/onnxruntime-osx-arm64-${ONNX_VERSION}.tgz" -o "${TEMP_DIR}/darwin-arm64.tgz"
|
||||
tar -xzf "${TEMP_DIR}/darwin-arm64.tgz" -C "${TEMP_DIR}"
|
||||
cp "${TEMP_DIR}/onnxruntime-osx-arm64-${ONNX_VERSION}/lib/libonnxruntime.${ONNX_VERSION}.dylib" "${ASSETS_DIR}/darwin-arm64/libonnxruntime.dylib"
|
||||
write_version_file "darwin-arm64"
|
||||
}
|
||||
|
||||
download_linux_amd64() {
|
||||
@@ -46,6 +81,7 @@ download_linux_amd64() {
|
||||
tar -xzf "${TEMP_DIR}/linux-amd64.tgz" -C "${TEMP_DIR}"
|
||||
cp "${TEMP_DIR}/onnxruntime-linux-x64-${ONNX_VERSION}/lib/libonnxruntime.so.${ONNX_VERSION}" "${ASSETS_DIR}/linux-amd64/libonnxruntime.so"
|
||||
cp "${TEMP_DIR}/onnxruntime-linux-x64-${ONNX_VERSION}/lib/libonnxruntime_providers_shared.so" "${ASSETS_DIR}/linux-amd64/libonnxruntime_providers_shared.so" 2>/dev/null || true
|
||||
write_version_file "linux-amd64"
|
||||
}
|
||||
|
||||
download_linux_arm64() {
|
||||
@@ -55,6 +91,7 @@ download_linux_arm64() {
|
||||
tar -xzf "${TEMP_DIR}/linux-arm64.tgz" -C "${TEMP_DIR}"
|
||||
cp "${TEMP_DIR}/onnxruntime-linux-aarch64-${ONNX_VERSION}/lib/libonnxruntime.so.${ONNX_VERSION}" "${ASSETS_DIR}/linux-arm64/libonnxruntime.so"
|
||||
cp "${TEMP_DIR}/onnxruntime-linux-aarch64-${ONNX_VERSION}/lib/libonnxruntime_providers_shared.so" "${ASSETS_DIR}/linux-arm64/libonnxruntime_providers_shared.so" 2>/dev/null || true
|
||||
write_version_file "linux-arm64"
|
||||
}
|
||||
|
||||
download_windows_amd64() {
|
||||
@@ -66,6 +103,7 @@ download_windows_amd64() {
|
||||
echo "Downloaded file size: $(wc -c < "${TEMP_DIR}/windows-amd64.zip") bytes"
|
||||
unzip -q "${TEMP_DIR}/windows-amd64.zip" -d "${TEMP_DIR}"
|
||||
cp "${TEMP_DIR}/onnxruntime-win-x64-${ONNX_VERSION}/lib/onnxruntime.dll" "${ASSETS_DIR}/windows-amd64/onnxruntime.dll"
|
||||
write_version_file "windows-amd64"
|
||||
}
|
||||
|
||||
# Check if library already exists for a platform
|
||||
@@ -79,19 +117,40 @@ lib_exists() {
|
||||
esac
|
||||
}
|
||||
|
||||
# Download only if not present
|
||||
# Download only if not present or version mismatch
|
||||
download_if_needed() {
|
||||
local plat="$1"
|
||||
if lib_exists "$plat"; then
|
||||
echo "Library for ${plat} already exists, skipping download"
|
||||
return 0
|
||||
local need_download=false
|
||||
local reason=""
|
||||
|
||||
if [ "$FORCE_DOWNLOAD" = true ]; then
|
||||
need_download=true
|
||||
reason="forced"
|
||||
elif ! lib_exists "$plat"; then
|
||||
need_download=true
|
||||
reason="not found"
|
||||
elif ! version_matches "$plat"; then
|
||||
local installed_version
|
||||
installed_version=$(get_installed_version "$plat")
|
||||
need_download=true
|
||||
reason="version mismatch (installed: ${installed_version:-unknown}, required: ${ONNX_VERSION})"
|
||||
fi
|
||||
|
||||
if [ "$need_download" = true ]; then
|
||||
if [ -n "$reason" ] && [ "$reason" != "not found" ]; then
|
||||
echo "Re-downloading ${plat}: ${reason}"
|
||||
fi
|
||||
# Remove old library before downloading
|
||||
rm -rf "${ASSETS_DIR}/${plat}"
|
||||
case "$plat" in
|
||||
darwin-arm64) download_darwin_arm64 ;;
|
||||
linux-amd64) download_linux_amd64 ;;
|
||||
linux-arm64) download_linux_arm64 ;;
|
||||
windows-amd64) download_windows_amd64 ;;
|
||||
esac
|
||||
else
|
||||
echo "Library for ${plat} already exists (v${ONNX_VERSION}), skipping download"
|
||||
fi
|
||||
case "$plat" in
|
||||
darwin-arm64) download_darwin_arm64 ;;
|
||||
linux-amd64) download_linux_amd64 ;;
|
||||
linux-arm64) download_linux_arm64 ;;
|
||||
windows-amd64) download_windows_amd64 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
echo "ONNX Runtime v${ONNX_VERSION} - Platform: ${PLATFORM}"
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "claude-mnemonic-dashboard",
|
||||
"version": "v0.6.5-23-g21bcbfa",
|
||||
"version": "v0.6.5-25-g372942e-dirty",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "claude-mnemonic-dashboard",
|
||||
"version": "v0.6.5-23-g21bcbfa",
|
||||
"version": "v0.6.5-25-g372942e-dirty",
|
||||
"dependencies": {
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mnemonic-dashboard",
|
||||
"version": "v0.6.5-23-g21bcbfa",
|
||||
"version": "v0.6.5-25-g372942e-dirty",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
Reference in New Issue
Block a user