/**
 * Wiki Search Service
 *
 * In-process full-text search across a wiki vault's `.md` files. Returns
 * per-file matches with line numbers + short snippets so the UI can render
 * a result list and let the user jump straight to a page.
 *
 * Ranking: Okapi **BM25** (term-frequency saturation via `k1`, length
 * normalisation via `b`, inverse-document-frequency weighting) computed
 * per request. No external index — the vaults here are O(100s) of pages so
 * a per-request scan + score is fine; revisit with a persisted index when
 * volumes justify it. Tokenisation is Unicode-aware: Latin word tokens plus
 * individual CJK characters, so Chinese/Japanese content is searchable.
 *
 * Scope: walks the vault directory AND the per-team overlay sources
 * (`sop/` → installed/custom SOPs, `team-norm/` → team norms) so SOP and
 * norm content is searchable even though it lives outside the vault.
 *
 * @module services/wiki/wiki-search.service
 */
/** Cap on how many .md files are inspected per search. */
export declare const WIKI_SEARCH_MAX_FILES = 500;
/** Cap on how many results returned to the caller. */
export declare const WIKI_SEARCH_MAX_RESULTS = 50;
/** Cap on how many snippets surfaced per matching file. */
export declare const WIKI_SEARCH_MAX_SNIPPETS_PER_FILE = 3;
/** Maximum query length — guards against pathological input. */
export declare const WIKI_SEARCH_MAX_QUERY_LENGTH = 200;
/** Cap on per-file size we will read; larger files are skipped. */
export declare const WIKI_SEARCH_MAX_FILE_BYTES: number;
export interface WikiSearchSnippet {
    /** 1-based line number where the match was found. */
    lineNumber: number;
    /** The matching line (trimmed for display). */
    text: string;
}
export interface WikiSearchHit {
    /** Relative path inside the vault, with forward slashes. */
    relativePath: string;
    /** True when the filename itself contains the query. */
    filenameMatch: boolean;
    /** Number of total content matches across the file. */
    matchCount: number;
    /** BM25 relevance score (higher = better). */
    score: number;
    /** Up to `WIKI_SEARCH_MAX_SNIPPETS_PER_FILE` matching lines. */
    snippets: WikiSearchSnippet[];
    /** Owning vault path (set for all-vault search; absent for single-vault). */
    vaultPath?: string;
    /** Human label for the owning vault (all-vault search only). */
    vaultLabel?: string;
}
export interface WikiSearchResult {
    ok: true;
    /** Searched vault path, or '' for an all-vault search. */
    vaultPath: string;
    query: string;
    hits: WikiSearchHit[];
    /** True when the file/result cap was hit; UI should hint "narrow your search". */
    truncated: boolean;
}
export interface WikiSearchFailure {
    ok: false;
    reason: 'vault_missing' | 'invalid_input' | 'invalid_query';
    message: string;
}
export interface WikiSearchInput {
    vaultPath: string;
    query: string;
}
/** A vault to include in an all-vault search. */
export interface VaultRef {
    vaultPath: string;
    label?: string;
}
export interface WikiMultiSearchInput {
    vaults: VaultRef[];
    query: string;
}
/**
 * Tokenise text into searchable terms: lowercase Latin word runs
 * (`[a-z0-9]+`) plus individual CJK characters (Han, Hiragana, Katakana),
 * so both English and Chinese/Japanese content is matched.
 *
 * @param text - Raw text to tokenise.
 * @returns Array of lowercase tokens (order preserved, duplicates kept).
 */
export declare function tokenize(text: string): string[];
/**
 * Service exposing `search()`. Stateless singleton — matches the pattern of
 * the other wiki services (`WikiBookkeepService.getInstance()`, etc.).
 */
export declare class WikiSearchService {
    private static instance;
    /** Singleton accessor. */
    static getInstance(): WikiSearchService;
    /** Reset for tests. */
    static resetInstance(): void;
    /** Validate + normalise a query, or return a structured failure. */
    private validateQuery;
    /**
     * Search a single vault and rank results with BM25. Returns either
     * `{ok:true, hits}` or a structured failure. Filesystem read errors are
     * silently skipped so one bad file doesn't blank the whole result.
     */
    search(input: WikiSearchInput): Promise<WikiSearchResult | WikiSearchFailure>;
    /**
     * Search across MANY vaults and return one merged BM25-ranked list. IDF is
     * computed over the combined corpus so scores are comparable across vaults.
     * Each hit carries its owning `vaultPath` (+ `vaultLabel` when provided).
     */
    searchAllVaults(input: WikiMultiSearchInput): Promise<WikiSearchResult | WikiSearchFailure>;
    /**
     * BM25-rank an explicit list of documents (rather than auto-collecting the
     * whole vault). Lets callers that need their own eligibility rules — e.g. the
     * `wiki-query` skill, which ranks only `llm-curated/` pages and skips
     * `log.md` — reuse the exact same Okapi BM25 scoring with IDF computed over
     * precisely the documents they supply.
     *
     * @param vaultPath - Owning vault (stamped on each hit's `vaultPath`).
     * @param docs - Candidate documents as `{ relativePath, absPath }`.
     * @param query - Raw query string (validated/normalised here).
     * @returns BM25-ranked hits, or an empty array for an invalid/empty query.
     */
    searchCorpus(vaultPath: string, docs: Array<{
        relativePath: string;
        absPath: string;
    }>, query: string): Promise<WikiSearchHit[]>;
    /**
     * Core BM25 search over an arbitrary corpus of doc refs (one or many vaults).
     * Builds the combined corpus statistics, scores, ranks, and attaches snippets
     * for the top results. Each hit carries its owning `vaultPath`.
     */
    private _searchCorpus;
    /**
     * Collect candidate `.md` documents: the vault tree plus the per-team
     * overlay sources (`sop/`, `team-norm/`). Overlay paths are prefixed with
     * the folder name so they resolve back through the page endpoint.
     *
     * @param vaultPath - Absolute vault root.
     * @returns Deduped list of `{ relativePath, absPath }`, capped at the limit.
     */
    private collectDocs;
    /**
     * Recursively collect `.md` files under `root`, returned as root-relative
     * POSIX paths. Skips dotfile dirs (`.git`, `.queue`) and stops at the cap.
     */
    private walkMd;
    /**
     * Read a doc and compute its length + per-query-term frequencies. Filename
     * tokens are folded in (weighted) so title relevance contributes to BM25.
     * Returns null only on unreadable/oversize files with no filename match.
     */
    private statDoc;
    /**
     * Standard Okapi BM25 score for a doc against the query terms.
     */
    private bm25Score;
    /**
     * Re-read a top-ranked file and extract matching-line snippets (a line
     * matches when it contains any query term as a substring). Returns the
     * total content match count + up to the snippet cap.
     */
    private buildSnippets;
}
//# sourceMappingURL=wiki-search.service.d.ts.map