search

package
v0.17.13 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package search provides cross-session search indexing capabilities. It builds an index of session conversations for fast semantic lookup without importing pkg/agent to avoid circular dependencies.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultIndexPath

func DefaultIndexPath() string

DefaultIndexPath returns the default location for the search index file. It returns ~/.sprout/sessions/search-index.json. If $HOME cannot be determined the function returns an empty string and the caller should handle the error appropriately.

func FormatResults

func FormatResults(results []SearchResult) string

FormatResults renders search results as a human-readable text block suitable for CLI output. Each result is formatted as:

[YYYY-MM-DD] name — working_dir
  excerpt

Results are separated by a single blank line. If the slice is empty, the function returns "No matching sessions."

func InitGlobalUpdater

func InitGlobalUpdater(indexPath, sessionsDir string)

InitGlobalUpdater lazily initializes the process-global IndexUpdater. Subsequent calls are no-ops (sync.Once).

func MarkSessionDirty

func MarkSessionDirty(sessionID string)

MarkSessionDirty is a convenience wrapper that calls GlobalUpdater.MarkDirty. If the global updater hasn't been initialized yet, it does nothing (safe no-op).

func RestoreGlobalUpdater

func RestoreGlobalUpdater(old *IndexUpdater)

RestoreGlobalUpdater puts the global back to its prior state after a test called ResetGlobalUpdaterForTest. Stops the current global first.

func SaveIndex

func SaveIndex(path string, idx *SessionIndex) error

SaveIndex atomically writes the search index to disk.

The index is first written to path+".tmp" with 0600 permissions, synced to disk via Close(), then renamed into place. Parent directories are created if they do not exist. idx.BuiltAt is updated to the current time before serialisation.

func WalkSessions

func WalkSessions(sessionsDir string) ([]string, error)

WalkSessions returns a sorted list of all session_*.json files found under sessionsDir (recursive). If the directory does not exist an empty slice is returned (not an error).

Types

type IndexUpdater

type IndexUpdater struct {
	// contains filtered or unexported fields
}

IndexUpdater debounces index writes to avoid disk thrash.

var (
	GlobalUpdater *IndexUpdater
)

func NewIndexUpdater

func NewIndexUpdater(indexPath, sessionsDir string) *IndexUpdater

NewIndexUpdater creates an updater that writes to indexPath from sessionsDir.

func ResetGlobalUpdaterForTest

func ResetGlobalUpdaterForTest() (old *IndexUpdater)

ResetGlobalUpdaterForTest stops the current global updater (if any), then re-initializes the sync.Once so subsequent InitGlobalUpdater calls take effect. Returns the previous updater so callers can restore it. Intended for tests that need to redirect the index to a temp dir.

Note: the underlying *IndexUpdater may still hold pending timer events scheduled with the OLD path. The Stop() call here cancels them.

func (*IndexUpdater) Flush

func (u *IndexUpdater) Flush() error

Flush forces an immediate rebuild + save (e.g. on shutdown).

func (*IndexUpdater) MarkDirty

func (u *IndexUpdater) MarkDirty(sessionID string)

MarkDirty marks a session ID as needing an index update. Coalesces with previous uncommitted marks. Schedules a debounced rebuild.

func (*IndexUpdater) Stop

func (u *IndexUpdater) Stop()

Stop cancels any pending timer.

type SearchOptions

type SearchOptions struct {
	// Query is the search string (required).  Whitespace-separated tokens
	// are treated as individual terms; the full query is also tested as an
	// exact phrase.
	Query string

	// WorkingDir restricts results to entries whose WorkingDir exactly
	// matches this value.  Empty string means no filter.
	WorkingDir string

	// Since limits results to entries with LastUpdated >= Since.  Zero
	// value disables the filter.
	Since time.Time

	// Until limits results to entries with LastUpdated <= Until.  Zero
	// value disables the filter.
	Until time.Time

	// Limit caps the number of returned results.  Zero uses the default
	// of 20.
	Limit int
}

SearchOptions configures a search query against a SessionIndex.

type SearchResult

type SearchResult struct {
	SessionID   string    `json:"session_id"`
	Name        string    `json:"name"`
	WorkingDir  string    `json:"working_directory"`
	LastUpdated time.Time `json:"last_updated"`
	TotalCost   float64   `json:"total_cost"`
	Excerpt     string    `json:"excerpt"`
	MatchScore  int       `json:"match_score"` // 1 (any term), 2 (all terms), 3 (exact phrase)
}

SearchResult is a single matched session with a formatted excerpt.

func Search(idx *SessionIndex, opts SearchOptions) []SearchResult

Search runs the query against the index and returns ranked results.

Results are scored in three tiers (higher is better):

  • 3: the full query phrase appears in the entry's text.
  • 2: every whitespace-separated term appears (but not necessarily adjacent).
  • 1: at least one term appears.

Ties are broken by recency (newer LastUpdated first). Filters (WorkingDir, Since, Until) are applied before ranking so that only eligible entries are considered.

type SessionIndex

type SessionIndex struct {
	Version  int                          `json:"version"`
	BuiltAt  time.Time                    `json:"built_at"`
	Sessions map[string]SessionIndexEntry `json:"sessions"`
}

SessionIndex is the top-level search index structure.

func BuildIndex

func BuildIndex(sessionsDir string, idx *SessionIndex) (*SessionIndex, error)

BuildIndex walks the sessions directory and builds (or updates) the SessionIndex for every session_*.json file it finds.

The walk is recursive to discover scoped sub-directories under the sessions base (e.g. ~/.sprout/sessions/scoped/<hash>/).

Incremental update: if idx already has an entry for a session whose LastUpdated timestamp matches the file's mtime, that entry is kept without re-parsing. Entries whose session files no longer exist on disk are removed from the index.

idx.Version is set to 1 and idx.BuiltAt is updated to now.

func LoadIndex

func LoadIndex(path string) (*SessionIndex, error)

LoadIndex reads and parses the search index from the given path.

If the file does not exist a zero-value SessionIndex with an initialised (non-nil) Sessions map is returned — not an error. Malformed JSON returns the underlying parse error.

type SessionIndexEntry

type SessionIndexEntry struct {
	SessionID    string           `json:"session_id"`
	Name         string           `json:"name"`
	WorkingDir   string           `json:"working_directory"`
	LastUpdated  time.Time        `json:"last_updated"`
	TotalCost    float64          `json:"total_cost"`
	MessageCount int              `json:"message_count"`
	Tokens       map[string][]int `json:"tokens"` // [start, end] byte offsets in Text
	Text         string           `json:"text"`   // Concatenated user/assistant messages, lowercased
}

SessionIndexEntry holds indexed data for a single session.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL