// tools/manage-context/store.ts — Low-level storage helpers for context notes
import { readFile, readdir, unlink } from 'node:fs/promises';
import { join } from 'node:path';
import { projectDataDir } from '../../storage/index.js';
import { writeJsonSafe } from '../../engine/safety/atomic-writer.js';
// ---------------------------------------------------------------------------
// Directory helpers
// ---------------------------------------------------------------------------
/** Directory where context notes are stored for a project. */
export function contextDir(projectId) {
    return join(projectDataDir(projectId), 'context');
}
/** Directory where tagged notes (with frontmatter) are stored. */
export function notesDir(projectId) {
    return join(projectDataDir(projectId), 'notes');
}
// ---------------------------------------------------------------------------
// Key / filename helpers
// ---------------------------------------------------------------------------
/** Sanitize a key to use as a safe filename (no path traversal). */
export function sanitizeKey(key) {
    return key
        .replace(/[^a-zA-Z0-9_\-. ]/g, '_')
        .replace(/\s+/g, '-')
        .replace(/_{2,}/g, '_')
        .replace(/^\.+/, '')
        .slice(0, 128);
}
/** Generate a short preview from content (first 120 chars of first line). */
export function preview(content) {
    /* v8 ignore next */
    const firstLine = content.split('\n').find((l) => l.trim().length > 0) ?? '';
    const clean = firstLine.replace(/^#+\s*/, '').trim();
    return clean.length > 120 ? clean.slice(0, 117) + '...' : clean;
}
// ---------------------------------------------------------------------------
// Metadata sidecar
// ---------------------------------------------------------------------------
/** Read the metadata sidecar for a note. Returns null if not found. */
export async function readMeta(projectId, safeKey) {
    const metaPath = join(contextDir(projectId), `${safeKey}.meta.json`);
    try {
        const raw = await readFile(metaPath, 'utf-8');
        return JSON.parse(raw);
    }
    catch {
        return null;
    }
}
/** Write the metadata sidecar for a note. */
export async function writeMeta(projectId, safeKey, meta) {
    const metaPath = join(contextDir(projectId), `${safeKey}.meta.json`);
    await writeJsonSafe(metaPath, meta);
}
// ---------------------------------------------------------------------------
// Note file operations
// ---------------------------------------------------------------------------
/** Delete both the .md and .meta.json files for a note. */
export async function deleteNote(projectId, safeKey) {
    const dir = contextDir(projectId);
    const mdPath = join(dir, `${safeKey}.md`);
    const metaPath = join(dir, `${safeKey}.meta.json`);
    try {
        await unlink(mdPath);
    }
    catch {
        // ignore if not found
    }
    try {
        await unlink(metaPath);
    }
    catch {
        // ignore if not found
    }
}
/** List all note keys in the context directory. */
export async function listNoteKeys(projectId) {
    const dir = contextDir(projectId);
    try {
        const files = await readdir(dir);
        return files.filter((f) => f.endsWith('.md')).map((f) => f.replace(/\.md$/, ''));
    }
    catch {
        return [];
    }
}
/** List all note filenames in the notes/ directory. */
export async function listNoteFilenames(projectId) {
    const dir = notesDir(projectId);
    try {
        const files = await readdir(dir);
        return files.filter((f) => f.endsWith('.md'));
    }
    catch {
        return [];
    }
}
// ---------------------------------------------------------------------------
// Expiration
// ---------------------------------------------------------------------------
/** Check if a note has expired. */
export function isExpired(meta) {
    if (!meta.expiresAt) {
        return false;
    }
    return new Date(meta.expiresAt) < new Date();
}
// ---------------------------------------------------------------------------
// Tag / priority helpers
// ---------------------------------------------------------------------------
/** Priority order for NoteTag-based sorting. */
export const TAG_PRIORITY = {
    blocker: 0,
    pending: 1,
    bug: 2,
    decision: 3,
    idea: 4,
};
/** Get the minimum priority tag from a list of tags. Lower number = higher priority. */
export function getMinPriority(tags) {
    if (tags.length === 0) {
        return 99;
    }
    return Math.min(...tags.map((t) => TAG_PRIORITY[t]));
}
// ---------------------------------------------------------------------------
// Frontmatter
// ---------------------------------------------------------------------------
/** Generate YAML frontmatter for a tagged note. */
export function generateFrontmatter(tags, specId, createdAt) {
    const tagLine = tags.length > 0 ? `tags: [${tags.join(', ')}]` : 'tags: []';
    const specLine = specId ? `specId: ${specId}` : '';
    const lines = ['---', tagLine];
    if (specLine) {
        lines.push(specLine);
    }
    lines.push(`createdAt: ${createdAt}`);
    lines.push('---');
    return lines.join('\n');
}
/** Parse YAML frontmatter from a note's .md content. Returns null if none. */
export function parseFrontmatter(content) {
    if (!content.startsWith('---')) {
        return null;
    }
    const end = content.indexOf('\n---', 3);
    if (end === -1) {
        return null;
    }
    const block = content.slice(3, end).trim();
    const tags = [];
    let specId;
    let createdAt;
    let resolvedAt;
    for (const line of block.split('\n')) {
        const trimmed = line.trim();
        if (trimmed.startsWith('tags:')) {
            const raw = trimmed.slice(5).trim().replace(/^\[/, '').replace(/\]$/, '');
            for (const t of raw.split(',').map((s) => s.trim())) {
                if (t === 'pending' || t === 'decision' || t === 'idea' || t === 'bug' || t === 'blocker') {
                    tags.push(t);
                }
            }
        }
        else if (trimmed.startsWith('specId:')) {
            specId = trimmed.slice(7).trim();
        }
        else if (trimmed.startsWith('createdAt:')) {
            createdAt = trimmed.slice(10).trim();
        }
        else if (trimmed.startsWith('resolvedAt:')) {
            resolvedAt = trimmed.slice(11).trim();
        }
    }
    return { tags, specId, createdAt, resolvedAt };
}
//# sourceMappingURL=store.js.map