/**
 * Skill activation header (Level-3 progressive disclosure).
 *
 * When a skill is activated — either by user `/<name>` invocation or by the
 * agent calling `activate_skill` — the model gets a small synthetic header
 * prepended to the SKILL.md body that tells it:
 *
 *   1. The absolute path to the skill's root directory (so it can read
 *      referenced files using the relative paths the SKILL.md author wrote).
 *   2. A one-level-deep manifest of `references/`, `scripts/`, `assets/` so
 *      the model knows what's available without recursive directory scans.
 *
 * The header is wrapped in an HTML comment fence so:
 *   - it's markdown-invisible (graceful fallback if a UI strip ever misses);
 *   - it's anchored at `^\s*` for an unambiguous regex strip;
 *   - it's still plain text the model can read.
 *
 * For sandboxed sessions, host paths to non-project skills (built-in /
 * personal) cannot be read inside the container. In that case we emit a
 * "degraded" header that tells the model the skill root is not accessible,
 * with no resource manifest. Project-local skills (under the worktree)
 * still work because the worktree mounts at `/workspace`; the caller passes
 * a `pathRewrite` callback that returns the container-side path for those.
 */
import fs from "node:fs";
import path from "node:path";
const HEADER_OPEN = "<!-- skill-activation-header -->";
const HEADER_CLOSE = "<!-- /skill-activation-header -->";
const HEADER_MAX_BYTES = 2 * 1024;
const RESOURCE_DIRS = ["references", "scripts", "assets"];
/**
 * Scan a skill root for one-level-deep entries inside `references/`,
 * `scripts/`, `assets/`. Returns null if none of those dirs exist (so the
 * caller can decide to omit the resource line). Subdirectories within the
 * three roots are NOT recursed into.
 */
export function buildSkillResourceManifest(skillRoot) {
    if (!skillRoot || !fs.existsSync(skillRoot))
        return null;
    const collected = [];
    let anyDir = false;
    for (const sub of RESOURCE_DIRS) {
        const dir = path.join(skillRoot, sub);
        let stat = null;
        try {
            stat = fs.statSync(dir);
        }
        catch { /* missing */ }
        if (!stat || !stat.isDirectory())
            continue;
        anyDir = true;
        let entries;
        try {
            entries = fs.readdirSync(dir, { withFileTypes: true });
        }
        catch {
            continue;
        }
        for (const e of entries) {
            // One-level-deep only: include files; include subdirs as `name/` markers
            // are NOT useful to the model — skip them. Only files at the first level.
            if (!e.isFile())
                continue;
            collected.push(`${sub}/${e.name}`);
        }
    }
    if (!anyDir)
        return null;
    collected.sort((a, b) => a.localeCompare(b));
    // Cap total joined string at HEADER_MAX_BYTES (UTF-8 byte length).
    // Always express paths joined with ", ".
    const SEP = ", ";
    const kept = [];
    let bytes = 0;
    let truncated = false;
    for (let i = 0; i < collected.length; i++) {
        const item = collected[i];
        const candidate = (kept.length ? SEP : "") + item;
        const candidateBytes = Buffer.byteLength(candidate, "utf-8");
        if (bytes + candidateBytes > HEADER_MAX_BYTES) {
            truncated = true;
            break;
        }
        bytes += candidateBytes;
        kept.push(item);
    }
    const more = collected.length - kept.length;
    return {
        root: skillRoot,
        resources: kept,
        truncated,
        truncationSuffix: truncated && more > 0 ? `(${more} more files)` : undefined,
    };
}
/**
 * Build the activation header for a skill. Returns `""` (empty string) when
 * no header should be emitted — i.e. the skill has no on-disk root (legacy
 * `.claude/commands/*.md` files and synthetic built-ins like `compact`).
 *
 * Examples:
 *   <!-- skill-activation-header -->
 *   Skill root: /abs/path/to/skill
 *   Available resources: references/REFERENCE.md, scripts/extract.py
 *   <!-- /skill-activation-header -->
 *
 * Sandboxed (non-rewritable):
 *   <!-- skill-activation-header -->
 *   Skill root: (not visible inside sandbox — see docs/internals.md "Sandbox skill visibility")
 *   <!-- /skill-activation-header -->
 *
 * The header always ends with a blank line so the SKILL.md body that
 * follows starts on its own line.
 */
export function buildActivationHeader(skill, pathRewrite) {
    // Skip header for legacy `.claude/commands/*.md` (no skill dir — single
    // file, not a directory) and synthetic built-ins (`filePath === "(built-in)"`).
    if (!skill.filePath || skill.filePath === "(built-in)")
        return "";
    if (skill.source === "legacy")
        return "";
    // SKILL.md sits inside the skill root directory.
    const fileBase = path.basename(skill.filePath).toLowerCase();
    if (fileBase !== "skill.md")
        return "";
    const hostRoot = path.dirname(skill.filePath);
    let displayRoot;
    let manifest;
    if (pathRewrite) {
        const rewritten = pathRewrite(hostRoot);
        if (rewritten === null) {
            // Degraded header — no resources, root not accessible.
            return [
                HEADER_OPEN,
                `Skill root: (not visible inside sandbox — see docs/internals.md "Sandbox skill visibility")`,
                HEADER_CLOSE,
                "",
            ].join("\n");
        }
        displayRoot = rewritten;
        // Manifest still scans the host (caller is on host); container path
        // only affects the displayed root string.
        manifest = buildSkillResourceManifest(hostRoot);
    }
    else {
        displayRoot = hostRoot;
        manifest = buildSkillResourceManifest(hostRoot);
    }
    const lines = [HEADER_OPEN, `Skill root: ${displayRoot}`];
    if (manifest && manifest.resources.length > 0) {
        let line = `Available resources: ${manifest.resources.join(", ")}`;
        if (manifest.truncationSuffix)
            line += ` ${manifest.truncationSuffix}`;
        lines.push(line);
    }
    lines.push(HEADER_CLOSE, "");
    return lines.join("\n");
}
/** Regex used to strip the activation header at chip render time. Anchored at `^\s*`.
 *
 * NOTE: An identical inline copy lives in `src/ui/components/SkillChip.ts`.
 * Importing this constant into the UI bundle would drag server-only modules
 * (`node:fs`, `node:path`) along, so the duplication is intentional. **Keep
 * the two patterns in sync.** */
export const ACTIVATION_HEADER_STRIP_RE = /^\s*<!--\s*skill-activation-header\s*-->[\s\S]*?<!--\s*\/skill-activation-header\s*-->\s*/;
//# sourceMappingURL=skill-manifest.js.map