"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.lineCountOf = lineCountOf;
exports.listMemoryPrompts = listMemoryPrompts;
exports.toggleMemoryPrompt = toggleMemoryPrompt;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const agent_prompts_1 = require("./agent-prompts");
function memoryDir(folderPath) {
    return path.join(folderPath, '.ccweb', 'memory');
}
/** The START/END markers are emitted as plain-text lines — the user asked
 *  for this exact format so they remain easily searchable in CLAUDE.md. */
function markerStart(name) { return `START ${name}`; }
function markerEnd(name) { return `END ${name}`; }
/** Escape user-supplied memory name for use inside a RegExp. */
function reEscape(s) {
    return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
/** Regex matching one complete `START <name>\n<body>\nEND <name>` block,
 *  including any immediately surrounding blank lines, so removal collapses
 *  the space cleanly. */
function blockRegex(name) {
    const n = reEscape(name);
    // Eats: optional leading \n+, START <name> line, any body (non-greedy),
    //       END <name> line, optional trailing \n+.
    return new RegExp(`\\n*^START ${n}$[\\s\\S]*?^END ${n}$\\n*`, 'm');
}
function previewOf(content) {
    const line = content.split('\n').find((l) => l.trim()) ?? '';
    return line.trim().slice(0, 200);
}
/** Line count as shown to the user: strips a single trailing `\n` before
 *  counting, so "a\nb\n" (editor shows 2 lines) and "a\nb" both return 2. */
function lineCountOf(content) {
    if (content.length === 0)
        return 0;
    return content.replace(/\n$/, '').split('\n').length;
}
function listMemoryPrompts(folderPath, cliTool) {
    const dir = memoryDir(folderPath);
    let files = [];
    try {
        files = fs.readdirSync(dir).filter((f) => f.endsWith('.md'));
    }
    catch {
        // dir missing is normal — still return instructions file line count
        return { items: [], claudeMdLineCount: lineCountOf((0, agent_prompts_1.readClaudeMd)(folderPath, cliTool)) };
    }
    const claudeMd = (0, agent_prompts_1.readClaudeMd)(folderPath, cliTool);
    const out = [];
    for (const filename of files) {
        const name = filename.replace(/\.md$/, '');
        const filePath = path.join(dir, filename);
        // Skip symlinks to protect against a file in memory/ pointing at /etc etc.
        let stat;
        try {
            stat = fs.lstatSync(filePath);
        }
        catch {
            continue;
        }
        if (!stat || !stat.isFile())
            continue;
        let preview = '';
        let lineCount = 0;
        try {
            const content = fs.readFileSync(filePath, 'utf-8');
            preview = previewOf(content);
            lineCount = lineCountOf(content.replace(/\r\n/g, '\n'));
        }
        catch { /* unreadable — still list it with empty preview */ }
        const inserted = blockRegex(name).test(claudeMd);
        out.push({ filename, name, preview, inserted, lineCount });
    }
    out.sort((a, b) => a.name.localeCompare(b.name));
    return { items: out, claudeMdLineCount: lineCountOf(claudeMd) };
}
function toggleMemoryPrompt(folderPath, filename, action, cliTool) {
    // Guard filename — reject anything that could escape memory/ via the name.
    if (!filename || filename.includes('/') || filename.includes('\\') || filename.includes('\0')) {
        return { ok: false, changed: false, inserted: false, reason: 'invalid-filename' };
    }
    if (!filename.endsWith('.md')) {
        return { ok: false, changed: false, inserted: false, reason: 'not-md' };
    }
    const name = filename.replace(/\.md$/, '');
    const filePath = path.join(memoryDir(folderPath), filename);
    let claudeMd = (0, agent_prompts_1.readClaudeMd)(folderPath, cliTool);
    const re = blockRegex(name);
    const currentlyInserted = re.test(claudeMd);
    if (action === 'remove') {
        if (!currentlyInserted) {
            return { ok: true, changed: false, inserted: false, reason: 'not-present', claudeMdLineCount: lineCountOf(claudeMd) };
        }
        claudeMd = claudeMd.replace(re, '\n\n');
        // Collapse any runs of 3+ blank lines left behind into 2
        claudeMd = claudeMd.replace(/\n{3,}/g, '\n\n');
        (0, agent_prompts_1.writeClaudeMd)(folderPath, claudeMd, cliTool);
        return { ok: true, changed: true, inserted: false, claudeMdLineCount: lineCountOf(claudeMd) };
    }
    // Insert (or refresh in place).  Symlink guard mirrors `listMemoryPrompts`
    // so a client can't bypass the list's lstat filter by posting a filename
    // not currently in the list — e.g. an attacker placing `evil.md → /etc/...`
    // and toggling it directly would otherwise leak the target into CLAUDE.md.
    let stat;
    try {
        stat = fs.lstatSync(filePath);
    }
    catch {
        return { ok: false, changed: false, inserted: false, reason: 'file-not-found' };
    }
    if (!stat.isFile()) {
        return { ok: false, changed: false, inserted: false, reason: 'not-regular-file' };
    }
    let content;
    try {
        content = fs.readFileSync(filePath, 'utf-8');
    }
    catch {
        return { ok: false, changed: false, inserted: false, reason: 'file-not-found' };
    }
    // Normalize leading + trailing newlines so START/END aren't visually
    // glued to whatever the body happens to start/end with, and emit a blank
    // line BETWEEN the marker and the body on both sides — user-requested
    // format: START and END each stand alone, separated from surrounding
    // content (and from the body) by an empty line.
    const body = content.replace(/\r\n/g, '\n').replace(/^\n+|\n+$/g, '');
    const block = `${markerStart(name)}\n\n${body}\n\n${markerEnd(name)}`;
    if (currentlyInserted) {
        // Refresh: replace existing block with fresh content (content file may
        // have been edited since last insert).
        claudeMd = claudeMd.replace(re, `\n\n${block}\n\n`);
        claudeMd = claudeMd.replace(/\n{3,}/g, '\n\n');
        (0, agent_prompts_1.writeClaudeMd)(folderPath, claudeMd, cliTool);
        return { ok: true, changed: true, inserted: true, reason: 'refreshed', claudeMdLineCount: lineCountOf(claudeMd) };
    }
    // Append with separating blank line if instructions file doesn't already end with one
    const sep = claudeMd.length === 0 ? '' : claudeMd.endsWith('\n\n') ? '' : claudeMd.endsWith('\n') ? '\n' : '\n\n';
    claudeMd = claudeMd + sep + block + '\n';
    (0, agent_prompts_1.writeClaudeMd)(folderPath, claudeMd, cliTool);
    return { ok: true, changed: true, inserted: true, claudeMdLineCount: lineCountOf(claudeMd) };
}
//# sourceMappingURL=memory-prompts.js.map