Documentation
¶
Overview ¶
Package search orchestrates the FTS5-backed spec search index and exposes a single Search entry point shared by the TUI overlay and the CLI.
The package never opens the database itself: all SQL goes through internal/store accessors (AGENTS.md: only internal/store touches SQLite). It does parse markdown via internal/markdown, so the dependency arrow is store ← search → markdown, with store and markdown independent of each other.
Index ¶
Constants ¶
const ( ScopeAll = store.ScopeAll ScopeActive = store.ScopeActive ScopeArchived = store.ScopeArchived )
const DefaultLimit = 50
DefaultLimit caps the number of hits returned by Search when the caller does not specify one. The TUI overlay and CLI both leave it at this.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Hit ¶
type Hit struct {
SpecID string `json:"spec_id"`
Title string `json:"title"`
Status string `json:"status"`
Author string `json:"author,omitempty"`
Archived bool `json:"archived"`
SectionSlug string `json:"section_slug"`
SectionHeading string `json:"section_heading"`
Snippet string `json:"snippet"`
Score float64 `json:"score"`
}
Hit is one ranked, section-anchored search result. Both the TUI overlay and `spec search --json` consume this shape.
func KeywordScan ¶
func KeywordScan(rc *config.ResolvedConfig, keyword string) []Hit
KeywordScan performs a literal keyword substring scan (the relocated cmd/search.go logic) and returns unranked hits. `spec context` uses this so its keyword-search behaviour is unchanged after the searchDir helper moved out of cmd/.
type Indexer ¶
type Indexer struct {
// contains filtered or unexported fields
}
Indexer scans the specs repo (active + archive) and keeps the FTS5 index in sync, then answers ranked queries. One instance per process; the TUI keeps it for the lifetime of the App.
func NewIndexer ¶
func NewIndexer(rc *config.ResolvedConfig, db *store.DB) *Indexer
NewIndexer builds an Indexer over the resolved config and shared store. A nil db is tolerated: Search degrades to the live fallback scan and Reconcile is a no-op. This keeps the TUI working when store.Open failed.
func (*Indexer) IndexEmpty ¶
IndexEmpty reports whether the FTS index has never been populated, so the overlay can show its `indexing…` chip on a cold open. Nil-safe.
func (*Indexer) Reconcile ¶
Reconcile scans the specs repo (active + archive) and re-indexes only the files whose mtime changed since the last index (a hash check then skips mtime-only touches), removes index rows for files gone from disk, and returns aggregate stats. It is safe to call repeatedly — on an unchanged repo it does only the directory scans plus a diff and returns near-zero work. A single unparseable spec is counted in Stats.Failed and skipped; it never aborts the pass.
func (*Indexer) Search ¶
Search answers a ranked, section-anchored query. When the index has never been populated (cold start) it transparently falls back to a live substring scan over the same scope, so search is never broken while the background reconcile catches up. A nil db always uses the fallback.
type Options ¶
type Options struct {
Scope SearchScope
Limit int // 0 → DefaultLimit
}
Options tunes a Search call.
type SearchScope ¶
type SearchScope = store.SearchScope
SearchScope is re-exported from store so callers depend on search, not store, for the public surface.
type Stats ¶
type Stats struct {
Scanned int `json:"scanned"`
Reindexed int `json:"reindexed"`
Deleted int `json:"deleted"`
Skipped int `json:"skipped"` // unchanged (mtime+hash match)
Failed int `json:"failed"` // parse failures skipped, not fatal
Elapsed time.Duration `json:"elapsed"`
}
Stats summarises one Reconcile pass. The overlay shows `indexing…` while in flight and the CLI prints the final numbers for `--reindex`.