"use strict";
/**
 * Module: utils/task-package
 *
 * Encapsulated Taskdocs (`*.tsk/`).
 *
 * A Taskdoc is a directory ending in `.tsk`. It *may* contain:
 * - goals.md
 * - constraints.md
 * - progress.md
 *
 * These files are considered high-integrity state and MUST NOT be accessible via general file tools.
 */
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.BEAR_IN_MIND_SECTIONS = void 0;
exports.isTaskPackagePath = isTaskPackagePath;
exports.taskPackageSectionFromSelector = taskPackageSectionFromSelector;
exports.bearInMindSectionFromSelector = bearInMindSectionFromSelector;
exports.parseTaskPackageChangeMindTarget = parseTaskPackageChangeMindTarget;
exports.taskPackageFilenameForSection = taskPackageFilenameForSection;
exports.bearInMindFilenameForSection = bearInMindFilenameForSection;
exports.updateTaskPackageByChangeMindTarget = updateTaskPackageByChangeMindTarget;
exports.readTaskPackageByChangeMindTarget = readTaskPackageByChangeMindTarget;
exports.createTaskPackageByChangeMindTarget = createTaskPackageByChangeMindTarget;
exports.appendTaskPackageByChangeMindTarget = appendTaskPackageByChangeMindTarget;
exports.deleteTaskPackageByChangeMindTarget = deleteTaskPackageByChangeMindTarget;
exports.computeTaskPackageContentHash = computeTaskPackageContentHash;
exports.taskPackageRelativePathForChangeMindTarget = taskPackageRelativePathForChangeMindTarget;
exports.ensureTaskPackage = ensureTaskPackage;
exports.readTaskPackageSections = readTaskPackageSections;
exports.formatEffectiveTaskDocFromSections = formatEffectiveTaskDocFromSections;
exports.validateTaskPackageLayout = validateTaskPackageLayout;
exports.readTaskPackageForInjection = readTaskPackageForInjection;
exports.updateTaskPackageSection = updateTaskPackageSection;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const zlib_1 = require("zlib");
const async_fifo_mutex_1 = require("../runtime/async-fifo-mutex");
const taskPackageIdentifierRe = /^[a-zA-Z][a-zA-Z0-9_-]*(?:\.[a-zA-Z0-9_-]+)*$/;
function isTaskPackageIdentifier(value) {
    return taskPackageIdentifierRe.test(value);
}
exports.BEAR_IN_MIND_SECTIONS = [
    'contracts',
    'acceptance',
    'grants',
    'runbook',
    'decisions',
    'risks',
];
const taskPackageFileLocks = new Map();
function getTaskPackageFileLock(filePath) {
    const key = path.resolve(filePath);
    const existing = taskPackageFileLocks.get(key);
    if (existing)
        return existing;
    const created = new async_fifo_mutex_1.AsyncFifoMutex();
    taskPackageFileLocks.set(key, created);
    return created;
}
function clearTaskPackageFileLockIfIdle(filePath, lock) {
    const key = path.resolve(filePath);
    if (taskPackageFileLocks.get(key) === lock && !lock.isLocked()) {
        taskPackageFileLocks.delete(key);
    }
}
async function withTaskPackageFileLock(filePath, fn) {
    const lock = getTaskPackageFileLock(filePath);
    const release = await lock.acquire();
    try {
        return await fn();
    }
    finally {
        release();
        clearTaskPackageFileLockIfIdle(filePath, lock);
    }
}
const sectionToFilename = {
    goals: 'goals.md',
    constraints: 'constraints.md',
    progress: 'progress.md',
};
const bearInMindSectionToFilename = {
    contracts: 'contracts.md',
    acceptance: 'acceptance.md',
    grants: 'grants.md',
    runbook: 'runbook.md',
    decisions: 'decisions.md',
    risks: 'risks.md',
};
function normalizeTaskPackagePath(taskDocPath) {
    return taskDocPath.replace(/\\/g, '/').replace(/\/+$/g, '');
}
function isTaskPackagePath(taskDocPath) {
    return normalizeTaskPackagePath(taskDocPath).endsWith('.tsk');
}
function taskPackageSectionFromSelector(selector) {
    const normalized = selector.startsWith('!') ? selector.slice(1) : selector;
    if (normalized === 'goals')
        return normalized;
    if (normalized === 'constraints')
        return normalized;
    if (normalized === 'progress')
        return normalized;
    return null;
}
function bearInMindSectionFromSelector(selector) {
    const normalized = selector.startsWith('!') ? selector.slice(1) : selector;
    if (normalized === 'contracts')
        return normalized;
    if (normalized === 'acceptance')
        return normalized;
    if (normalized === 'grants')
        return normalized;
    if (normalized === 'runbook')
        return normalized;
    if (normalized === 'decisions')
        return normalized;
    if (normalized === 'risks')
        return normalized;
    return null;
}
function parseTaskPackageChangeMindTarget(params) {
    const selector = params.selector.trim();
    if (selector === '') {
        return { kind: 'err', error: { kind: 'selector_required' } };
    }
    const categoryValue = typeof params.category === 'string' ? params.category.trim() : '';
    const category = categoryValue !== '' ? categoryValue : null;
    // Category missing/empty => top-level only.
    if (category === null) {
        const topLevel = taskPackageSectionFromSelector(selector);
        if (topLevel !== null) {
            return { kind: 'ok', target: { kind: 'top_level', section: topLevel } };
        }
        return { kind: 'err', error: { kind: 'invalid_top_level_selector', selector } };
    }
    // Category present => validate category name first.
    if (!isTaskPackageIdentifier(category)) {
        return { kind: 'err', error: { kind: 'invalid_category_name', category } };
    }
    if (category === 'bearinmind') {
        const bear = bearInMindSectionFromSelector(selector);
        if (bear !== null) {
            return { kind: 'ok', target: { kind: 'bearinmind', section: bear } };
        }
        // Reserved top-level selectors must not be nested under any category.
        const topLevel = taskPackageSectionFromSelector(selector);
        if (topLevel !== null) {
            return {
                kind: 'err',
                error: { kind: 'top_level_selector_requires_no_category', category, selector },
            };
        }
        return { kind: 'err', error: { kind: 'invalid_bearinmind_selector', selector } };
    }
    // Reserved top-level selectors must not be nested under any category.
    const topLevel = taskPackageSectionFromSelector(selector);
    if (topLevel !== null) {
        return {
            kind: 'err',
            error: { kind: 'top_level_selector_requires_no_category', category, selector },
        };
    }
    // Reserved bearinmind selectors must only appear under category="bearinmind".
    const bear = bearInMindSectionFromSelector(selector);
    if (bear !== null) {
        return {
            kind: 'err',
            error: { kind: 'bearinmind_selector_requires_bearinmind_category', category, selector },
        };
    }
    const normalizedSelector = selector.startsWith('!') ? selector.slice(1) : selector;
    if (!isTaskPackageIdentifier(normalizedSelector)) {
        return { kind: 'err', error: { kind: 'invalid_category_selector', selector } };
    }
    return {
        kind: 'ok',
        target: { kind: 'category', category, selector: normalizedSelector },
    };
}
function taskPackageFilenameForSection(section) {
    return sectionToFilename[section];
}
function bearInMindFilenameForSection(section) {
    return bearInMindSectionToFilename[section];
}
async function updateTaskPackageByChangeMindTarget(params) {
    const { taskPackageDirFullPath, target, content, previousContentHash } = params;
    const filePath = taskPackageFilePathForChangeMindTarget(taskPackageDirFullPath, target);
    return await withTaskPackageFileLock(filePath, async () => {
        try {
            const st = await fs.promises.stat(filePath);
            if (!st.isFile()) {
                throw new Error(`Taskdoc section target is not a file: ${filePath}`);
            }
            const currentContent = await fs.promises.readFile(filePath, 'utf8');
            const currentContentHash = computeTaskPackageContentHash(currentContent);
            if (currentContentHash !== previousContentHash) {
                return { kind: 'content_hash_mismatch', currentContentHash };
            }
            await writeExistingMarkdownFileWithCanonicalEnding(filePath, content);
            return { kind: 'updated' };
        }
        catch (err) {
            if (typeof err === 'object' &&
                err !== null &&
                'code' in err &&
                err.code === 'ENOENT') {
                return { kind: 'missing' };
            }
            throw err;
        }
    });
}
async function readTaskPackageByChangeMindTarget(params) {
    const filePath = taskPackageFilePathForChangeMindTarget(params.taskPackageDirFullPath, params.target);
    return await withTaskPackageFileLock(filePath, async () => {
        const content = await readTextFileIfPresent(filePath);
        if (content === null)
            return { kind: 'missing' };
        return { kind: 'present', content, contentHash: computeTaskPackageContentHash(content) };
    });
}
async function createTaskPackageByChangeMindTarget(params) {
    const { taskPackageDirFullPath, target, content, updatedBy } = params;
    await ensureTaskPackage(taskPackageDirFullPath, updatedBy);
    const filePath = taskPackageFilePathForChangeMindTarget(taskPackageDirFullPath, target);
    return await withTaskPackageFileLock(filePath, async () => {
        await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
        try {
            await writeNewMarkdownFileWithCanonicalEnding(filePath, content);
            return { kind: 'created' };
        }
        catch (err) {
            if (typeof err === 'object' &&
                err !== null &&
                'code' in err &&
                err.code === 'EEXIST') {
                return { kind: 'exists' };
            }
            throw err;
        }
    });
}
async function appendTaskPackageByChangeMindTarget(params) {
    const { taskPackageDirFullPath, target, content, sep, updatedBy } = params;
    await ensureTaskPackage(taskPackageDirFullPath, updatedBy);
    const filePath = taskPackageFilePathForChangeMindTarget(taskPackageDirFullPath, target);
    await withTaskPackageFileLock(filePath, async () => {
        await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
        const current = await readTextFileIfPresent(filePath);
        const next = current === null || current.trim() === '' ? content : joinForAppend(current, content, sep);
        await writeMarkdownFileWithCanonicalEnding(filePath, next);
    });
}
async function deleteTaskPackageByChangeMindTarget(params) {
    const filePath = taskPackageFilePathForChangeMindTarget(params.taskPackageDirFullPath, params.target);
    return await withTaskPackageFileLock(filePath, async () => {
        try {
            const st = await fs.promises.stat(filePath);
            if (!st.isFile()) {
                throw new Error(`Taskdoc section target is not a file: ${filePath}`);
            }
            await fs.promises.unlink(filePath);
            return { kind: 'deleted' };
        }
        catch (err) {
            if (typeof err === 'object' &&
                err !== null &&
                'code' in err &&
                err.code === 'ENOENT') {
                return { kind: 'missing' };
            }
            throw err;
        }
    });
}
async function writeMarkdownFileWithCanonicalEnding(filePath, content) {
    await writeMarkdownFileWithCanonicalEndingViaRename(filePath, content, 'overwrite');
}
async function writeExistingMarkdownFileWithCanonicalEnding(filePath, content) {
    await writeMarkdownFileWithCanonicalEndingViaRename(filePath, content, 'replace-existing');
}
async function writeNewMarkdownFileWithCanonicalEnding(filePath, content) {
    await fs.promises.writeFile(filePath, withCanonicalMarkdownFileEnding(content), {
        encoding: 'utf8',
        flag: 'wx',
    });
}
async function writeMarkdownFileWithCanonicalEndingViaRename(filePath, content, mode) {
    const tempFilePath = path.join(path.dirname(filePath), `.${path.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
    if (mode === 'replace-existing') {
        const st = await fs.promises.stat(filePath);
        if (!st.isFile()) {
            throw new Error(`Taskdoc section target is not a file: ${filePath}`);
        }
    }
    try {
        await fs.promises.writeFile(tempFilePath, withCanonicalMarkdownFileEnding(content), {
            encoding: 'utf8',
            flag: 'wx',
        });
        await fs.promises.rename(tempFilePath, filePath);
    }
    catch (err) {
        await removeTempFileIfPresent(tempFilePath);
        throw err;
    }
}
async function removeTempFileIfPresent(filePath) {
    try {
        await fs.promises.unlink(filePath);
    }
    catch (err) {
        if (typeof err === 'object' &&
            err !== null &&
            'code' in err &&
            err.code === 'ENOENT') {
            return;
        }
        throw err;
    }
}
function withCanonicalMarkdownFileEnding(content) {
    const stripped = stripTrailingMarkdownFileNewlines(content);
    if (stripped === '')
        return '';
    return `${stripped}\n`;
}
function computeTaskPackageContentHash(content) {
    const checksum = (0, zlib_1.crc32)(withCanonicalMarkdownFileEnding(content)) >>> 0;
    return `crc32:${checksum.toString(16).padStart(8, '0')}`;
}
function taskPackageRelativePathForChangeMindTarget(target) {
    switch (target.kind) {
        case 'top_level':
            return taskPackageFilenameForSection(target.section);
        case 'bearinmind':
            return path.join('bearinmind', bearInMindFilenameForSection(target.section));
        case 'category':
            return path.join(target.category, `${target.selector}.md`);
        default: {
            const _exhaustive = target;
            return _exhaustive;
        }
    }
}
function taskPackageFilePathForChangeMindTarget(taskPackageDirFullPath, target) {
    return path.join(taskPackageDirFullPath, taskPackageRelativePathForChangeMindTarget(target));
}
async function readTextFileIfPresent(filePath) {
    try {
        return await fs.promises.readFile(filePath, 'utf8');
    }
    catch (err) {
        if (typeof err === 'object' &&
            err !== null &&
            'code' in err &&
            err.code === 'ENOENT') {
            return null;
        }
        throw err;
    }
}
function joinForAppend(current, content, sep) {
    if (current.endsWith(sep))
        return `${current}${content}`;
    for (let prefixLength = sep.length - 1; prefixLength > 0; prefixLength -= 1) {
        const prefix = sep.slice(0, prefixLength);
        if (current.endsWith(prefix)) {
            return `${current}${sep.slice(prefixLength)}${content}`;
        }
    }
    return `${current}${sep}${content}`;
}
async function fileExists(fullPath) {
    try {
        await fs.promises.stat(fullPath);
        return true;
    }
    catch (err) {
        if (typeof err === 'object' &&
            err !== null &&
            'code' in err &&
            err.code === 'ENOENT') {
            return false;
        }
        throw err;
    }
}
async function ensureTaskPackage(taskPackageDirFullPath, _updatedBy) {
    await fs.promises.mkdir(taskPackageDirFullPath, { recursive: true });
}
async function readSectionFile(sectionPath) {
    if (!(await fileExists(sectionPath))) {
        return { kind: 'missing' };
    }
    const content = await fs.promises.readFile(sectionPath, 'utf8');
    return { kind: 'present', content };
}
async function readTaskPackageSections(taskPackageDirFullPath) {
    const goals = await readSectionFile(path.join(taskPackageDirFullPath, taskPackageFilenameForSection('goals')));
    const constraints = await readSectionFile(path.join(taskPackageDirFullPath, taskPackageFilenameForSection('constraints')));
    const progress = await readSectionFile(path.join(taskPackageDirFullPath, taskPackageFilenameForSection('progress')));
    return { goals, constraints, progress };
}
function formatSectionBodyI18n(state) {
    // Injection must be deterministic and treat section bodies as opaque markdown,
    // except for file-final newlines that only encode on-disk formatting.
    // If a required file is missing, inject an empty body (status should be shown elsewhere).
    if (state.kind === 'present')
        return stripTrailingMarkdownFileNewlines(state.content);
    return '';
}
function formatBearInMindBody(state) {
    if (state.kind !== 'present')
        return null;
    const content = stripTrailingMarkdownFileNewlines(state.content);
    if (content.trim() === '')
        return null;
    return content;
}
function stripTrailingMarkdownFileNewlines(content) {
    return content.replace(/(?:\r\n?|\n)+$/u, '');
}
function formatEffectiveTaskDocFromSections(language, sections, bearInMind) {
    // Deterministic framing only; section bodies are treated as opaque markdown.
    if (language === 'zh') {
        const bearBlocks = [];
        if (bearInMind?.kind === 'present') {
            for (const section of exports.BEAR_IN_MIND_SECTIONS) {
                const body = formatBearInMindBody(bearInMind.sections[section]);
                if (!body)
                    continue;
                bearBlocks.push(`### ${bearInMindFilenameForSection(section)}`, body);
            }
        }
        const bearInMindBlock = bearBlocks.length > 0 ? [`## Bear In Mind`, ...bearBlocks, ``].join('\n') : '';
        return [
            `# 差遣牒（对话树）`,
            ``,
            `## Goals`,
            formatSectionBodyI18n(sections.goals),
            ``,
            `## Constraints`,
            formatSectionBodyI18n(sections.constraints),
            ``,
            ...(bearInMindBlock ? [bearInMindBlock] : []),
            `## Progress`,
            formatSectionBodyI18n(sections.progress),
        ].join('\n');
    }
    const bearBlocks = [];
    if (bearInMind?.kind === 'present') {
        for (const section of exports.BEAR_IN_MIND_SECTIONS) {
            const body = formatBearInMindBody(bearInMind.sections[section]);
            if (!body)
                continue;
            bearBlocks.push(`### ${bearInMindFilenameForSection(section)}`, body);
        }
    }
    const bearInMindBlock = bearBlocks.length > 0 ? [`## Bear In Mind`, ...bearBlocks, ``].join('\n') : '';
    return [
        `# Taskdoc (Dialog Tree)`,
        ``,
        `## Goals`,
        formatSectionBodyI18n(sections.goals),
        ``,
        `## Constraints`,
        formatSectionBodyI18n(sections.constraints),
        ``,
        ...(bearInMindBlock ? [bearInMindBlock] : []),
        `## Progress`,
        formatSectionBodyI18n(sections.progress),
    ].join('\n');
}
async function readBearInMindSections(taskPackageDirFullPath) {
    const bearDir = path.join(taskPackageDirFullPath, 'bearinmind');
    try {
        const st = await fs.promises.stat(bearDir);
        if (!st.isDirectory()) {
            return { kind: 'invalid', reason: 'not_a_directory' };
        }
    }
    catch (err) {
        if (typeof err === 'object' &&
            err !== null &&
            'code' in err &&
            err.code === 'ENOENT') {
            return { kind: 'absent' };
        }
        throw err;
    }
    const sections = {
        contracts: { kind: 'missing' },
        acceptance: { kind: 'missing' },
        grants: { kind: 'missing' },
        runbook: { kind: 'missing' },
        decisions: { kind: 'missing' },
        risks: { kind: 'missing' },
    };
    for (const section of exports.BEAR_IN_MIND_SECTIONS) {
        sections[section] = await readSectionFile(path.join(bearDir, bearInMindFilenameForSection(section)));
    }
    const extraEntries = [];
    const allowed = new Set(Object.values(bearInMindSectionToFilename));
    const entries = await fs.promises.readdir(bearDir, { withFileTypes: true });
    for (const entry of entries) {
        if (entry.name.startsWith('.'))
            continue;
        if (!allowed.has(entry.name)) {
            extraEntries.push(entry.name);
        }
    }
    return { kind: 'present', sections, extraEntries };
}
async function validateTaskPackageLayout(taskPackageDirFullPath) {
    const violations = [];
    const maxEntries = 2048;
    let seenEntries = 0;
    const topLevelFilenames = new Set(Object.values(sectionToFilename));
    const bearFilenames = new Set(Object.values(bearInMindSectionToFilename));
    const allowedBearDirEntries = new Set(Object.values(bearInMindSectionToFilename));
    const stack = [
        { absDir: taskPackageDirFullPath, relDir: '' },
    ];
    while (stack.length > 0) {
        const cur = stack.pop();
        if (!cur)
            break;
        let entries;
        try {
            entries = await fs.promises.readdir(cur.absDir, { withFileTypes: true });
        }
        catch {
            continue;
        }
        for (const entry of entries) {
            seenEntries++;
            if (seenEntries > maxEntries) {
                violations.push({ kind: 'scan_limit_exceeded', maxEntries });
                return violations;
            }
            const relPath = cur.relDir ? path.posix.join(cur.relDir, entry.name) : entry.name;
            const absPath = path.join(cur.absDir, entry.name);
            if (entry.isDirectory()) {
                if (cur.relDir === 'bearinmind') {
                    violations.push({ kind: 'bearinmind_extra_entry', foundAt: relPath });
                }
                stack.push({ absDir: absPath, relDir: relPath.replace(/\\/g, '/') });
                continue;
            }
            if (!entry.isFile()) {
                if (cur.relDir === 'bearinmind') {
                    violations.push({ kind: 'bearinmind_extra_entry', foundAt: relPath });
                }
                continue;
            }
            if (cur.relDir === 'bearinmind' && !allowedBearDirEntries.has(entry.name)) {
                violations.push({ kind: 'bearinmind_extra_entry', foundAt: relPath });
            }
            if (topLevelFilenames.has(entry.name) && cur.relDir !== '') {
                violations.push({
                    kind: 'top_level_file_under_subdir',
                    filename: entry.name,
                    foundAt: relPath,
                });
            }
            if (bearFilenames.has(entry.name)) {
                if (cur.relDir !== 'bearinmind') {
                    violations.push({
                        kind: 'bearinmind_file_outside_bearinmind',
                        filename: entry.name,
                        foundAt: relPath,
                    });
                }
            }
        }
    }
    // Special-case: bearinmind exists but is not a directory.
    const bearDir = path.join(taskPackageDirFullPath, 'bearinmind');
    try {
        const st = await fs.promises.stat(bearDir);
        if (!st.isDirectory()) {
            violations.push({ kind: 'bearinmind_not_directory', foundAt: 'bearinmind' });
        }
    }
    catch (err) {
        if (typeof err === 'object' &&
            err !== null &&
            'code' in err &&
            err.code === 'ENOENT') {
            // ok
        }
        else {
            // ignore unexpected validation errors to avoid blocking prompt injection
        }
    }
    return violations;
}
async function readTaskPackageForInjection(taskPackageDirFullPath) {
    const sections = await readTaskPackageSections(taskPackageDirFullPath);
    const bearInMind = await readBearInMindSections(taskPackageDirFullPath);
    const extraSections = await readTaskPackageExtraSectionsIndex(taskPackageDirFullPath);
    const violations = await validateTaskPackageLayout(taskPackageDirFullPath);
    return { sections, bearInMind, extraSections, violations };
}
async function updateTaskPackageSection(params) {
    const { taskPackageDirFullPath, section, content, updatedBy } = params;
    await ensureTaskPackage(taskPackageDirFullPath, updatedBy);
    const filePath = path.join(taskPackageDirFullPath, taskPackageFilenameForSection(section));
    await writeMarkdownFileWithCanonicalEnding(filePath, content);
}
async function readTaskPackageExtraSectionsIndex(taskPackageDirFullPath) {
    const maxEntries = 64;
    let total = 0;
    try {
        const dirents = await fs.promises.readdir(taskPackageDirFullPath, { withFileTypes: true });
        const categoryToSelectors = new Map();
        for (const entry of dirents) {
            if (!entry.isDirectory())
                continue;
            if (entry.name.startsWith('.'))
                continue;
            if (entry.name === 'bearinmind')
                continue;
            if (!isTaskPackageIdentifier(entry.name))
                continue;
            const category = entry.name;
            const catAbs = path.join(taskPackageDirFullPath, category);
            let catEntries;
            try {
                catEntries = await fs.promises.readdir(catAbs, { withFileTypes: true });
            }
            catch {
                continue;
            }
            for (const child of catEntries) {
                if (total >= maxEntries) {
                    return {
                        kind: 'present',
                        categories: materializeExtraCategories(categoryToSelectors),
                        truncated: true,
                    };
                }
                if (!child.isFile())
                    continue;
                if (child.name.startsWith('.'))
                    continue;
                if (!child.name.endsWith('.md'))
                    continue;
                const selector = child.name.slice(0, -3);
                if (!isTaskPackageIdentifier(selector))
                    continue;
                if (taskPackageSectionFromSelector(selector) !== null)
                    continue;
                if (bearInMindSectionFromSelector(selector) !== null)
                    continue;
                const set = categoryToSelectors.get(category) ?? new Set();
                set.add(selector);
                categoryToSelectors.set(category, set);
                total++;
            }
        }
        return {
            kind: 'present',
            categories: materializeExtraCategories(categoryToSelectors),
            truncated: false,
        };
    }
    catch {
        return { kind: 'unavailable', reason: 'scan_error' };
    }
}
function materializeExtraCategories(categoryToSelectors) {
    const categories = Array.from(categoryToSelectors.entries())
        .filter(([, selectors]) => selectors.size > 0)
        .map(([category, selectors]) => ({
        category,
        selectors: Array.from(selectors.values()).sort(),
    }))
        .sort((a, b) => a.category.localeCompare(b.category));
    return categories;
}
