mirror of
https://github.com/lukaszraczylo/claude-mnemonic.git
synced 2026-06-09 23:59:40 +00:00
7a061c85eb
* refactor(hooks): simplify hook execution with shared context - [x] Extract BaseInput struct to eliminate duplicate fields across hooks - [x] Create RunHook handler pattern for session-start and user-prompt - [x] Create RunStatuslineHook for fast statusline rendering without worker startup - [x] Add HookContext struct to pass port, project, CWD, SessionID to handlers - [x] Add db/interface.go with ObservationReader/Writer interfaces - [x] Add comprehensive conflict management tests in sqlite/conflict_test.go - [x] Add vector client tests for Count, ModelVersion, NeedsRebuild, GetStaleVectors - [x] Add FilterByThreshold helper tests for query result filtering - [x] Make handlers_test more robust for network-dependent update checks - [x] Update package versions in UI * Move to GORM + general cleanup * feat(mcp): add observation relations discovery and scoring integration - [x] Add find_related_observations MCP tool for discovering related observations by confidence - [x] Integrate scoring calculator and recalculator into MCP server initialization - [x] Add pattern, relation, and session stores to MCP server dependencies - [x] Register MCP server in Claude Code settings during plugin installation - [x] Update install scripts (bash, PowerShell) to configure MCP server settings - [x] Switch plugin manifest files to template-based versioning (plugin.json.tpl, marketplace.json.tpl) - [x] Update all MCP server tests to pass new dependency parameters
172 lines
4.7 KiB
Go
172 lines
4.7 KiB
Go
// Package gorm provides GORM-based database operations for claude-mnemonic.
|
|
package gorm
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/lukaszraczylo/claude-mnemonic/pkg/models"
|
|
)
|
|
|
|
// SummaryStore provides summary-related database operations using GORM.
|
|
type SummaryStore struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
// NewSummaryStore creates a new summary store.
|
|
func NewSummaryStore(store *Store) *SummaryStore {
|
|
return &SummaryStore{db: store.DB}
|
|
}
|
|
|
|
// StoreSummary stores a new session summary.
|
|
func (s *SummaryStore) StoreSummary(ctx context.Context, sdkSessionID, project string, summary *models.ParsedSummary, promptNumber int, discoveryTokens int64) (int64, int64, error) {
|
|
now := time.Now()
|
|
nowEpoch := now.UnixMilli()
|
|
|
|
// Ensure session exists (auto-create if missing)
|
|
if err := EnsureSessionExists(ctx, s.db, sdkSessionID, project); err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
dbSummary := &SessionSummary{
|
|
SDKSessionID: sdkSessionID,
|
|
Project: project,
|
|
Request: nullString(summary.Request),
|
|
Investigated: nullString(summary.Investigated),
|
|
Learned: nullString(summary.Learned),
|
|
Completed: nullString(summary.Completed),
|
|
NextSteps: nullString(summary.NextSteps),
|
|
Notes: nullString(summary.Notes),
|
|
PromptNumber: func() sql.NullInt64 {
|
|
if promptNumber > 0 {
|
|
return sql.NullInt64{Int64: int64(promptNumber), Valid: true}
|
|
}
|
|
return sql.NullInt64{Valid: false}
|
|
}(),
|
|
DiscoveryTokens: discoveryTokens,
|
|
CreatedAt: now.Format(time.RFC3339),
|
|
CreatedAtEpoch: nowEpoch,
|
|
}
|
|
|
|
err := s.db.WithContext(ctx).Create(dbSummary).Error
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
return dbSummary.ID, nowEpoch, nil
|
|
}
|
|
|
|
// GetSummariesByIDs retrieves summaries by a list of IDs.
|
|
func (s *SummaryStore) GetSummariesByIDs(ctx context.Context, ids []int64, orderBy string, limit int) ([]*models.SessionSummary, error) {
|
|
if len(ids) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
var dbSummaries []SessionSummary
|
|
query := s.db.WithContext(ctx).Where("id IN ?", ids)
|
|
|
|
// Apply ordering
|
|
switch orderBy {
|
|
case "date_asc":
|
|
query = query.Order("created_at_epoch ASC")
|
|
case "date_desc", "default", "":
|
|
query = query.Order("created_at_epoch DESC")
|
|
}
|
|
|
|
// Apply limit
|
|
if limit > 0 {
|
|
query = query.Limit(limit)
|
|
}
|
|
|
|
err := query.Find(&dbSummaries).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return toModelSessionSummaries(dbSummaries), nil
|
|
}
|
|
|
|
// GetRecentSummaries retrieves recent summaries for a project.
|
|
func (s *SummaryStore) GetRecentSummaries(ctx context.Context, project string, limit int) ([]*models.SessionSummary, error) {
|
|
var dbSummaries []SessionSummary
|
|
err := s.db.WithContext(ctx).
|
|
Where("project = ?", project).
|
|
Order("created_at_epoch DESC").
|
|
Limit(limit).
|
|
Find(&dbSummaries).Error
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return toModelSessionSummaries(dbSummaries), nil
|
|
}
|
|
|
|
// GetAllRecentSummaries retrieves recent summaries across all projects.
|
|
func (s *SummaryStore) GetAllRecentSummaries(ctx context.Context, limit int) ([]*models.SessionSummary, error) {
|
|
var dbSummaries []SessionSummary
|
|
err := s.db.WithContext(ctx).
|
|
Order("created_at_epoch DESC").
|
|
Limit(limit).
|
|
Find(&dbSummaries).Error
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return toModelSessionSummaries(dbSummaries), nil
|
|
}
|
|
|
|
// GetAllSummaries retrieves all summaries (for vector rebuild).
|
|
func (s *SummaryStore) GetAllSummaries(ctx context.Context) ([]*models.SessionSummary, error) {
|
|
var dbSummaries []SessionSummary
|
|
err := s.db.WithContext(ctx).
|
|
Order("id").
|
|
Find(&dbSummaries).Error
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return toModelSessionSummaries(dbSummaries), nil
|
|
}
|
|
|
|
// toModelSessionSummary converts a GORM SessionSummary to pkg/models.SessionSummary.
|
|
func toModelSessionSummary(s *SessionSummary) *models.SessionSummary {
|
|
return &models.SessionSummary{
|
|
ID: s.ID,
|
|
SDKSessionID: s.SDKSessionID,
|
|
Project: s.Project,
|
|
Request: s.Request,
|
|
Investigated: s.Investigated,
|
|
Learned: s.Learned,
|
|
Completed: s.Completed,
|
|
NextSteps: s.NextSteps,
|
|
Notes: s.Notes,
|
|
PromptNumber: s.PromptNumber,
|
|
DiscoveryTokens: s.DiscoveryTokens,
|
|
CreatedAt: s.CreatedAt,
|
|
CreatedAtEpoch: s.CreatedAtEpoch,
|
|
}
|
|
}
|
|
|
|
// toModelSessionSummaries converts a slice of GORM SessionSummary to pkg/models.SessionSummary.
|
|
func toModelSessionSummaries(summaries []SessionSummary) []*models.SessionSummary {
|
|
result := make([]*models.SessionSummary, len(summaries))
|
|
for i := range summaries {
|
|
result[i] = toModelSessionSummary(&summaries[i])
|
|
}
|
|
return result
|
|
}
|
|
|
|
// nullString converts a string to sql.NullString.
|
|
func nullString(s string) sql.NullString {
|
|
if s == "" {
|
|
return sql.NullString{Valid: false}
|
|
}
|
|
return sql.NullString{String: s, Valid: true}
|
|
}
|