/**
 * SOP Service
 *
 * Manages Standard Operating Procedures (SOPs) including loading,
 * indexing, matching, and creating custom SOPs.
 *
 * @module services/sop/sop.service
 */
import { SOP, SOPRole, SOPCategory, SOPIndex, SOPIndexEntry, SOPMatchParams } from '../../types/sop.types.js';
/**
 * Interface for the SOP Service
 */
export interface ISOPService {
    /**
     * Initialize the service (create directories, build index)
     */
    initialize(): Promise<void>;
    /**
     * Rebuild the SOP index from filesystem
     */
    rebuildIndex(): Promise<void>;
    /**
     * Get an SOP by ID
     *
     * @param id - SOP identifier
     * @returns SOP or null if not found
     */
    getSOP(id: string): Promise<SOP | null>;
    /**
     * Get all SOPs for a specific role
     *
     * @param role - Role to filter by
     * @returns Array of SOPs
     */
    getSOPsByRole(role: SOPRole | 'all'): Promise<SOP[]>;
    /**
     * Get all SOPs in a category
     *
     * @param category - Category to filter by
     * @returns Array of SOPs
     */
    getSOPsByCategory(category: SOPCategory): Promise<SOP[]>;
    /**
     * Find SOPs relevant to the given context
     *
     * @param params - Match parameters
     * @returns Array of matching SOPs
     */
    findRelevantSOPs(params: SOPMatchParams): Promise<SOP[]>;
    /**
     * Generate SOP context string for prompts
     *
     * @param params - Match parameters
     * @returns Formatted SOP context string
     */
    generateSOPContext(params: SOPMatchParams): Promise<string>;
    /**
     * Create a custom SOP
     *
     * @param sop - SOP data (without id, timestamps, version)
     * @returns Created SOP ID
     */
    createCustomSOP(sop: Omit<SOP, 'id' | 'createdAt' | 'updatedAt' | 'version'>): Promise<string>;
    /**
     * Update an existing SOP
     *
     * @param id - SOP identifier
     * @param updates - Partial SOP updates
     */
    updateSOP(id: string, updates: Partial<SOP>): Promise<void>;
    /**
     * Delete a custom SOP
     *
     * @param id - SOP identifier
     */
    deleteSOP(id: string): Promise<void>;
    /**
     * Get the current SOP index
     *
     * @returns SOP index
     */
    getIndex(): Promise<SOPIndex>;
    /**
     * Search SOPs by query string
     *
     * @param query - Search query
     * @returns Matching index entries
     */
    searchSOPs(query: string): Promise<SOPIndexEntry[]>;
    /**
     * Get the reason for the last graceful fallback, if any.
     *
     * Used by the API layer to surface degraded-mode telemetry to callers
     * (e.g. when the index file is missing and an empty in-memory fallback
     * was substituted). Returns null after any operation that successfully
     * read the index from disk.
     *
     * @returns Fallback reason string, or null if last operation served
     *   from real data on disk.
     */
    getLastFallbackReason(): string | null;
}
/**
 * Service for managing Standard Operating Procedures
 *
 * @example
 * ```typescript
 * const service = SOPService.getInstance();
 * await service.initialize();
 *
 * const sops = await service.findRelevantSOPs({
 *   role: 'developer',
 *   taskContext: 'implementing a new API endpoint',
 * });
 * ```
 */
export declare class SOPService implements ISOPService {
    private static instance;
    private readonly logger;
    private readonly basePath;
    private readonly systemPath;
    private readonly customPath;
    private readonly indexPath;
    private index;
    private sopCache;
    private initialized;
    private lastFallbackReason;
    /**
     * Sentinel reason emitted when the canonical index is missing/unparseable
     * and we synthesise an empty in-memory index so callers can degrade
     * cleanly instead of receiving a 500.
     *
     * Surfaced via `getLastFallbackReason()` and propagated by the API
     * controller to clients in the response envelope.
     */
    static readonly FALLBACK_REASON_INDEX_MISSING = "index-missing-graceful-fallback";
    /**
     * Private constructor for singleton pattern
     *
     * @param basePath - Optional custom base path for SOPs
     */
    private constructor();
    /**
     * Gets the singleton instance
     *
     * @returns The SOPService instance
     */
    static getInstance(): SOPService;
    /**
     * Clears the singleton instance (for testing)
     */
    static clearInstance(): void;
    /**
     * Create an instance with a custom base path (for testing)
     *
     * @param basePath - Custom base path
     * @returns SOPService instance
     */
    static createWithPath(basePath: string): SOPService;
    /**
     * Get the Crewly home directory
     *
     * @returns Home directory path
     */
    private getCrewlyHome;
    /**
     * Initialize the service
     */
    initialize(): Promise<void>;
    /**
     * Rebuild the SOP index from filesystem.
     *
     * Resilience contract (F8 — get-sops graceful fallback):
     *  - Walks `systemPath` and `customPath` to collect entries (each walker
     *    silently no-ops when its directory is absent).
     *  - Populates `this.index` with the collected entries (possibly empty)
     *    BEFORE attempting any disk write, so callers reading the index
     *    in-memory always see a valid object even if persistence fails.
     *  - Persists by ensuring `basePath` exists (`mkdir -p`) and then
     *    writing the index file. Persistence failure is logged-warn and
     *    swallowed — the in-memory index remains canonical for this process.
     *
     * Should not throw under normal failure modes (missing dir, ENOSPC,
     * permission to write index). Pathological errors in helpers are
     * logged inside them; this method returns successfully with an
     * in-memory index regardless.
     */
    rebuildIndex(): Promise<void>;
    /**
     * Ensure the SOP base directory exists before writing the index.
     *
     * Idempotent. Created with `recursive: true` so any missing parent
     * (e.g. a fresh `~/.crewly` install where `sops/` was never created)
     * is materialised in one call.
     */
    private ensureBasePathExists;
    /**
     * Index a directory recursively
     *
     * @param dirPath - Directory path to index
     * @param entries - Array to add entries to
     * @param isSystem - Whether these are system SOPs
     */
    private indexDirectory;
    /**
     * Parse an SOP file from disk
     *
     * @param filePath - Path to the SOP file
     * @returns Parsed SOP or null on error
     */
    private parseSOPFile;
    /**
     * Parse SOP content (frontmatter + body)
     *
     * @param content - Raw file content
     * @returns Parsed SOP or null on error
     */
    private parseSOPContent;
    /**
     * Convert parsed SOP to full SOP object
     *
     * @param parsed - Parsed SOP
     * @returns Full SOP object
     */
    private parsedToSOP;
    /**
     * Get an SOP by ID
     *
     * @param id - SOP identifier
     * @returns SOP or null if not found
     */
    getSOP(id: string): Promise<SOP | null>;
    /**
     * Get all SOPs for a specific role
     *
     * @param role - Role to filter by
     * @returns Array of SOPs
     */
    getSOPsByRole(role: SOPRole | 'all'): Promise<SOP[]>;
    /**
     * Get all SOPs in a category
     *
     * @param category - Category to filter by
     * @returns Array of SOPs
     */
    getSOPsByCategory(category: SOPCategory): Promise<SOP[]>;
    /**
     * Find SOPs relevant to the given context
     *
     * @param params - Match parameters
     * @returns Array of matching SOPs
     */
    findRelevantSOPs(params: SOPMatchParams): Promise<SOP[]>;
    /**
     * Check if an SOP matches a role
     *
     * @param entry - SOP index entry
     * @param role - Role to check
     * @returns True if matches
     */
    private matchesRole;
    /**
     * Score how relevant an SOP is to the given context
     *
     * @param entry - SOP index entry
     * @param context - Context string
     * @param taskType - Optional task type
     * @returns Relevance score (0-1)
     */
    scoreRelevance(entry: SOPIndexEntry, context: string, taskType?: string): number;
    /**
     * Generate SOP context string for prompts
     *
     * @param params - Match parameters
     * @returns Formatted SOP context string
     */
    generateSOPContext(params: SOPMatchParams): Promise<string>;
    /**
     * Read a team's installed/custom SOP library from
     * `~/.crewly/teams/<teamId>/sops/` (recursively). Each `.md` file becomes a
     * SOP entry with its frontmatter `title`/`category` (falling back to the
     * filename / parent folder). Read-only; tolerant of a missing directory.
     *
     * @param teamId - The team whose SOP library to read.
     * @returns Team SOP entries (possibly empty).
     */
    private readTeamSOPs;
    /**
     * Create a custom SOP
     *
     * @param sopData - SOP data (without id, timestamps, version)
     * @returns Created SOP ID
     */
    createCustomSOP(sopData: Omit<SOP, 'id' | 'createdAt' | 'updatedAt' | 'version'>): Promise<string>;
    /**
     * Update an existing SOP
     *
     * @param id - SOP identifier
     * @param updates - Partial SOP updates
     */
    updateSOP(id: string, updates: Partial<SOP>): Promise<void>;
    /**
     * Delete a custom SOP
     *
     * @param id - SOP identifier
     */
    deleteSOP(id: string): Promise<void>;
    /**
     * Get the current SOP index
     *
     * @returns SOP index
     */
    getIndex(): Promise<SOPIndex>;
    /**
     * Search SOPs by query string
     *
     * @param query - Search query
     * @returns Matching index entries
     */
    searchSOPs(query: string): Promise<SOPIndexEntry[]>;
    /**
     * Ensure the index exists (load from disk or rebuild from filesystem).
     *
     * Graceful-fallback contract (F8):
     *  - Happy path: read `index.json`, JSON.parse it, set `this.index`.
     *    `lastFallbackReason` is cleared.
     *  - Index missing/unparseable: invoke `rebuildIndex()`. If rebuild
     *    succeeded with N>=0 entries, mark fallback reason iff the index
     *    was rebuilt due to missing-on-disk (so the controller can surface
     *    `index-missing-graceful-fallback` to clients).
     *  - Catastrophic rebuild failure (extremely unlikely — rebuild itself
     *    swallows persistence errors): synthesise an empty in-memory index
     *    and stamp `lastFallbackReason`. The endpoint returns 200 with an
     *    empty payload rather than 500.
     */
    private ensureIndex;
    /**
     * Get the reason for the last graceful fallback, if any.
     *
     * Returns the `FALLBACK_REASON_INDEX_MISSING` sentinel when the most
     * recent index load fell back to a regenerated/synthesised in-memory
     * index. Returns null when the cached index was loaded directly from
     * a healthy `index.json` on disk.
     *
     * @returns Fallback reason string, or null.
     */
    getLastFallbackReason(): string | null;
    /**
     * Save the index to disk
     */
    private saveIndex;
    /**
     * Convert an SOP to Markdown format
     *
     * @param sop - SOP to convert
     * @returns Markdown string
     */
    private sopToMarkdown;
    /**
     * Clear the SOP cache
     */
    clearCache(): void;
}
//# sourceMappingURL=sop.service.d.ts.map