import { existsSync, realpathSync, readFileSync, statSync } from 'node:fs';
import { homedir } from 'node:os';
import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
import { getDataDir } from '../storage/json-store.js';
import { readWorkspacePrompt } from './workspace-prompt.js';
const INSTRUCTION_FILENAMES = ['CLAUDE.md', 'claude.md', 'AGENTS.md', 'agents.md'];
const MAX_FILE_CHARS = 48_000;
const MAX_TOTAL_CHARS = 120_000;
export function prependPersistentAgentContext(prompt, options) {
    const context = buildPersistentAgentContext(options);
    if (!context)
        return prompt;
    return `${context}\n\n${prompt}`;
}
export function buildPersistentAgentContext(options) {
    const { instructionContext } = buildPersistentAgentContextDetails(options);
    const sections = [];
    if (instructionContext)
        sections.push(instructionContext);
    if (options.includeWorkspacePrompt !== false) {
        const workspacePrompt = readWorkspacePrompt(options.workspaceId).trim();
        if (workspacePrompt) {
            sections.push([
                'Workspace prompt:',
                workspacePrompt,
            ].join('\n'));
        }
    }
    return sections.join('\n\n');
}
export function buildPersistentAgentContextDetails(options) {
    const instructionFiles = collectInstructionFiles(options.workingDir, options.boundDirs, {
        excludeNativeClaudeMd: options.excludeNativeClaudeMd,
    });
    return {
        instructionContext: buildInstructionFileContextFromFiles(instructionFiles),
        summary: buildPersistentAgentContextSummary(instructionFiles),
    };
}
function buildInstructionFileContext(workingDir, boundDirs, options = {}) {
    const files = collectInstructionFiles(workingDir, boundDirs, options);
    return buildInstructionFileContextFromFiles(files);
}
function buildInstructionFileContextFromFiles(files) {
    if (files.length === 0)
        return '';
    const lines = [
        'Persistent agent instructions:',
        'The following CLAUDE.md and AGENTS.md files were auto-loaded. Earlier files are lower priority; later files are closer to the current working directory and take precedence.',
    ];
    let usedChars = 0;
    for (const file of files) {
        if (usedChars >= MAX_TOTAL_CHARS) {
            lines.push('', '[Additional instruction files omitted because the persistent context budget was reached.]');
            break;
        }
        const remaining = MAX_TOTAL_CHARS - usedChars;
        const content = truncateForBudget(file.content, Math.min(MAX_FILE_CHARS, remaining));
        usedChars += content.length;
        lines.push('', `--- ${file.label} ---`, content);
    }
    return lines.join('\n').trim();
}
function buildPersistentAgentContextSummary(files) {
    const instructionFiles = files.map((file) => ({
        path: file.path,
        label: file.label,
        filename: file.filename,
    }));
    const counts = instructionFiles.reduce((acc, file) => {
        acc.total += 1;
        if (file.filename === 'CLAUDE.md' || file.filename === 'claude.md') {
            acc.claudeMd += 1;
        }
        if (file.filename === 'AGENTS.md' || file.filename === 'agents.md') {
            acc.agentsMd += 1;
        }
        return acc;
    }, {
        claudeMd: 0,
        agentsMd: 0,
        total: 0,
    });
    return { instructionFiles, counts };
}
function collectInstructionFiles(workingDir, boundDirs, options = {}) {
    const cwd = resolve(workingDir || process.cwd());
    const seen = new Set();
    const files = [];
    const filenames = getInstructionFilenames(options);
    for (const globalPath of globalInstructionPaths(options)) {
        addInstructionFile(files, seen, globalPath, compactPath(globalPath));
    }
    for (const dir of ancestorDirs(cwd, resolveInstructionRoot(cwd, boundDirs))) {
        for (const filename of filenames) {
            const fullPath = join(dir, filename);
            addInstructionFile(files, seen, fullPath, relativeOrAbsolute(cwd, fullPath));
        }
    }
    return files;
}
function globalInstructionPaths(options) {
    const dataDir = getDataDir();
    return getInstructionFilenames(options).map((filename) => join(dataDir, filename));
}
function getInstructionFilenames(options) {
    if (!options.excludeNativeClaudeMd)
        return INSTRUCTION_FILENAMES;
    return INSTRUCTION_FILENAMES.filter((filename) => filename !== 'CLAUDE.md');
}
function resolveInstructionRoot(cwd, boundDirs) {
    const roots = (boundDirs ?? [])
        .map((dir) => safeResolve(dir))
        .filter((dir) => Boolean(dir))
        .filter((dir) => isPathWithin(cwd, dir))
        .sort((a, b) => a.length - b.length);
    if (roots.length > 0)
        return roots[0];
    return findNearestProjectRoot(cwd);
}
function findNearestProjectRoot(cwd) {
    let current = cwd;
    while (true) {
        if (existsSync(join(current, '.git')) || existsSync(join(current, 'package.json'))) {
            return current;
        }
        const parent = dirname(current);
        if (parent === current)
            return cwd;
        current = parent;
    }
}
function ancestorDirs(cwd, root) {
    const dirs = [];
    let current = cwd;
    while (true) {
        dirs.push(current);
        if (current === root)
            break;
        const parent = dirname(current);
        if (parent === current)
            break;
        current = parent;
    }
    return dirs.reverse();
}
function addInstructionFile(files, seen, path, label) {
    if (!existsSync(path))
        return;
    const stat = statSync(path);
    if (!stat.isFile())
        return;
    const realPath = realpathSync(path);
    if (seen.has(realPath))
        return;
    seen.add(realPath);
    files.push({
        path: realPath,
        label,
        filename: basename(realPath),
        content: readFileSync(path, 'utf-8').trim(),
    });
}
function truncateForBudget(content, maxChars) {
    if (content.length <= maxChars)
        return content;
    const suffix = '\n\n[Instruction file truncated because the persistent context budget was reached.]';
    return `${content.slice(0, Math.max(0, maxChars - suffix.length)).trimEnd()}${suffix}`;
}
function safeResolve(path) {
    if (!path.trim())
        return null;
    return resolve(isAbsolute(path) ? path : join(process.cwd(), path));
}
function isPathWithin(path, root) {
    const rel = relative(root, path);
    return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
}
function relativeOrAbsolute(fromDir, path) {
    const rel = relative(fromDir, path);
    if (!rel.startsWith('..') && !isAbsolute(rel))
        return rel || path;
    return path;
}
function compactPath(path) {
    const home = homedir();
    return path.startsWith(home) ? `~${path.slice(home.length)}` : path;
}
//# sourceMappingURL=persistent-agent-context.js.map