// Package models contains domain models for claude-mnemonic. package models import ( "database/sql" "encoding/json" "time" ) // SessionSummary represents a summary of a Claude Code session. type SessionSummary struct { CreatedAt string `db:"created_at" json:"created_at"` SDKSessionID string `db:"sdk_session_id" json:"sdk_session_id"` Project string `db:"project" json:"project"` Completed sql.NullString `db:"completed" json:"completed,omitempty"` Investigated sql.NullString `db:"investigated" json:"investigated,omitempty"` Learned sql.NullString `db:"learned" json:"learned,omitempty"` NextSteps sql.NullString `db:"next_steps" json:"next_steps,omitempty"` Notes sql.NullString `db:"notes" json:"notes,omitempty"` Request sql.NullString `db:"request" json:"request,omitempty"` PromptNumber sql.NullInt64 `db:"prompt_number" json:"prompt_number,omitempty"` ID int64 `db:"id" json:"id"` DiscoveryTokens int64 `db:"discovery_tokens" json:"discovery_tokens"` CreatedAtEpoch int64 `db:"created_at_epoch" json:"created_at_epoch"` } // ParsedSummary represents a summary parsed from SDK response XML. type ParsedSummary struct { Request string Investigated string Learned string Completed string NextSteps string Notes string } // NewSessionSummary creates a new session summary from parsed data. func NewSessionSummary(sdkSessionID, project string, parsed *ParsedSummary, promptNumber int, discoveryTokens int64) *SessionSummary { now := time.Now() return &SessionSummary{ SDKSessionID: sdkSessionID, Project: project, Request: sql.NullString{String: parsed.Request, Valid: parsed.Request != ""}, Investigated: sql.NullString{String: parsed.Investigated, Valid: parsed.Investigated != ""}, Learned: sql.NullString{String: parsed.Learned, Valid: parsed.Learned != ""}, Completed: sql.NullString{String: parsed.Completed, Valid: parsed.Completed != ""}, NextSteps: sql.NullString{String: parsed.NextSteps, Valid: parsed.NextSteps != ""}, Notes: sql.NullString{String: parsed.Notes, Valid: parsed.Notes != ""}, PromptNumber: sql.NullInt64{Int64: int64(promptNumber), Valid: promptNumber > 0}, DiscoveryTokens: discoveryTokens, CreatedAt: now.Format(time.RFC3339), CreatedAtEpoch: now.UnixMilli(), } } // SessionSummaryJSON is a JSON-friendly representation of SessionSummary. // It converts sql.NullString to plain strings for clean JSON output. type SessionSummaryJSON struct { Completed string `json:"completed,omitempty"` SDKSessionID string `json:"sdk_session_id"` Project string `json:"project"` Request string `json:"request,omitempty"` Investigated string `json:"investigated,omitempty"` Learned string `json:"learned,omitempty"` NextSteps string `json:"next_steps,omitempty"` Notes string `json:"notes,omitempty"` CreatedAt string `json:"created_at"` ID int64 `json:"id"` PromptNumber int64 `json:"prompt_number,omitempty"` DiscoveryTokens int64 `json:"discovery_tokens"` CreatedAtEpoch int64 `json:"created_at_epoch"` } // MarshalJSON implements json.Marshaler for SessionSummary. // Converts sql.NullString fields to plain strings. func (s *SessionSummary) MarshalJSON() ([]byte, error) { j := SessionSummaryJSON{ ID: s.ID, SDKSessionID: s.SDKSessionID, Project: s.Project, DiscoveryTokens: s.DiscoveryTokens, CreatedAt: s.CreatedAt, CreatedAtEpoch: s.CreatedAtEpoch, } if s.Request.Valid { j.Request = s.Request.String } if s.Investigated.Valid { j.Investigated = s.Investigated.String } if s.Learned.Valid { j.Learned = s.Learned.String } if s.Completed.Valid { j.Completed = s.Completed.String } if s.NextSteps.Valid { j.NextSteps = s.NextSteps.String } if s.Notes.Valid { j.Notes = s.Notes.String } if s.PromptNumber.Valid { j.PromptNumber = s.PromptNumber.Int64 } return json.Marshal(j) }