/**
 * WikiBookkeepService — vault health metrics + consolidation signals.
 *
 * Per Steve's 2026-05-22 design point #5: "还要时不时让 agent 针对自己的
 * vault 进行 bookkeeping (可以根据存入的 md 数量来 trigger), 譬如过去 N
 * 天超过 X 个 md, 然后总结一下."
 *
 * This service produces the report the bookkeeping agent reads:
 *   - md count per top-level llm-curated subfolder
 *   - new mds in the last N days
 *   - filename clusters (likely-duplicates by Jaccard of token sets)
 *   - queue stats (so the agent sees pending items too)
 *
 * The agent uses this report (+ its LLM) to decide:
 *   - which pages to consolidate / dedupe (calls wiki-ingest with a new
 *     consolidated body + deletes the originals via a separate skill —
 *     deletion is OUT of scope for Phase 1; surface as proposals).
 *   - which topics deserve a summary "rollup" page.
 *
 * The service does NOT delete or rewrite anything. It reports.
 *
 * @module services/wiki/wiki-bookkeep.service
 */
import { SchemaLoaderService } from './schema-loader.service.js';
import { WikiQueueService } from './wiki-queue.service.js';
export interface WikiBookkeepInput {
    /** Absolute vault path. */
    vaultPath: string;
    /** Window for "recent activity" stats. Default 7 days. */
    windowDays?: number;
    /** Md-count threshold used by `shouldFire` (default 10). */
    threshold?: number;
    /**
     * Total md count the caller last acted on for this vault (its persisted
     * baseline / high-water mark). When provided, `shouldFire` is driven by
     * NET-NEW pages since the baseline (`totalMdCount - baselineMdCount`)
     * rather than by mtime-recency. This is the robust signal: an mtime
     * window counts every file a bulk operation (e.g. a vault migration that
     * writes 500 pages at once) just touched as "recent", so it fires forever;
     * net-new only counts genuinely-added pages. Omit for a stateless report
     * (falls back to the mtime-window `recentMdCount`).
     */
    baselineMdCount?: number;
}
export interface WikiPageRef {
    path: string;
    bytes: number;
    modifiedAt: string;
}
export interface WikiDuplicateCluster {
    /** Shared base — the longest filename prefix across the cluster. */
    basis: string;
    /** Member pages (relative paths). */
    pages: string[];
}
export interface WikiBookkeepReport {
    vault: {
        scope: string;
        id: string;
        path: string;
    };
    generatedAt: string;
    windowDays: number;
    threshold: number;
    /** Did we cross the threshold for "trigger a consolidation pass"? */
    shouldFire: boolean;
    /** Total md files under the vault (recursive). */
    totalMdCount: number;
    /** Newly created/touched mds within `windowDays` (mtime-based; informational). */
    recentMdCount: number;
    /**
     * Net-new pages since the caller's baseline (`max(0, total - baseline)`).
     * Equals `recentMdCount` when no baseline was supplied. This is what
     * `shouldFire` is computed from.
     */
    netNewMdCount: number;
    /** Per-folder counts under llm-curated/<x>/. */
    countsByFolder: Record<string, number>;
    /** Likely-duplicate clusters by filename Jaccard. */
    duplicateCandidates: WikiDuplicateCluster[];
    /** Mds that haven't been touched in 90 days — stale candidates. */
    staleCount: number;
    /** Queue snapshot for this vault. */
    queue: {
        pending: number;
        claimed: number;
        processed: number;
        skipped: number;
        total: number;
    };
    /** Recommendations the agent's LLM should act on. */
    recommendations: string[];
}
export type WikiBookkeepOutcome = {
    ok: true;
    report: WikiBookkeepReport;
} | {
    ok: false;
    reason: 'invalid_input' | 'schema_missing' | 'vault_missing';
    message: string;
};
/**
 * Vault health + bookkeeping report generator. Stateless; uses the
 * schema loader + queue services it composes.
 */
export declare class WikiBookkeepService {
    private static instance;
    private readonly logger;
    private readonly schemaLoader;
    private readonly queue;
    constructor(schemaLoader?: SchemaLoaderService, queue?: WikiQueueService);
    static getInstance(): WikiBookkeepService;
    /** Test-only reset. */
    static _resetForTesting(): void;
    /**
     * Build the bookkeeping report. Caller (skill / cron / agent) decides
     * whether to act on `shouldFire`. The service never writes; it reports.
     */
    generate(input: WikiBookkeepInput): Promise<WikiBookkeepOutcome>;
    private collectPages;
    /**
     * Find filename clusters by Jaccard over name tokens.
     * Two pages with ≥ 0.6 overlap on their non-trivial filename tokens
     * are flagged as potential duplicates. Token = anything ≥ 3 chars
     * after splitting on `[-_./]`, lowercased, minus date prefix.
     */
    private findDuplicateClusters;
    private tokenizeFilename;
    private jaccard;
    private longestCommonPrefix;
    private buildRecommendations;
}
//# sourceMappingURL=wiki-bookkeep.service.d.ts.map