/**
 * RB-410 件1 — the Agent Skills (`SKILL.md`) directory loader.
 *
 * The open skills format (published at agentskills.io, reference implementation `anthropics/skills`) is
 * a directory per skill: `skill-name/SKILL.md` holding a `---` frontmatter block plus a Markdown body,
 * with optional `scripts/` / `references/` / `assets/` resource directories beside it. sema's
 * {@link SkillSpec} is already the same shape — frontmatter → `name`/`description`, body → `content`,
 * sibling resources → `files[]` — so the only missing piece was this reader. No engine mechanism
 * changes: the output is an ordinary `SkillSpec[]` a deployment hands to `TaskSpec.skills`.
 *
 * **Deployment-half helper, called BEFORE a task starts.** It reads the filesystem synchronously and is
 * NOT reachable from the run loop — the engine still never resolves a skill path at tool time, and never
 * writes. An embedder serving skills from a remote/sandboxed filesystem should read through its own
 * `ExecutionEnv` instead and construct `SkillSpec` objects directly; this helper is the local-disk case.
 *
 * **Trust contract is unchanged and is the caller's:** a skill IS instructions, so everything read here
 * (body and attachments alike) enters the model's context as CALLER-TRUSTED material. Pointing this at a
 * directory third parties can write to is the same as pasting their text into the system prompt. The
 * loader validates SHAPE (spec conformance), never intent.
 *
 * **`allowed-tools` / `disallowed-tools` are COPIED, never judged for what they mean.** Each frontmatter list reaches
 * {@link SkillSpec.allowedTools} / {@link SkillSpec.disallowedTools} entry by entry as the author wrote it — no
 * intersection with a roster, no projection onto a tool name, no de-duplication. Each entry must be a rule SPELLING — the
 * rule syntax's own test, `ruleSpellingOf` (a tool name, then at most one parenthesized rule) — or the skill is refused;
 * what a spelled entry means (a tool, a namespace, a command rule, a path, a wildcard that grants nothing) is read in ONE
 * place, the run's grant table, which announces what it cannot read. The deny list has a second spelling,
 * `disallowedTools`, read where `disallowed-tools` is absent or null (YAML's null is the key's absence); a hyphen key with
 * a value is the list and the alias is not looked at. The allow list has none. **Each list is read whole by ONE scanner,
 * {@link scanValue} (its JSDoc states the rules), or the skill is refused by name (`no_frontmatter`)** — never installed
 * shorter: a `disallowed-tools` that lost entries is a looser skill than the one its author wrote. The scanner reads YAML's
 * structure as YAML does — a comment, a flow's `,` / `]` and a mapping's `: ` are YAML's inside a rule's parentheses too —
 * and departs from what YAML and the reference's reader give only where the list is cut into entries, on purpose: the cut
 * counts a rule's parentheses by depth and refuses an entry whose parentheses are not one group closing it — unmatched
 * either way, or followed by more text (the reference's zone is boolean and installs such an entry); a `*` entry is
 * copied, the list not collapsed to `*` alone; tab-indented block items are read (the reference's retry reads them too);
 * and a block scalar's text is cut line by line — each line of a literal one (`|`) as written, a folded one (`>`) after
 * YAML's folding, so `>` lines that hold one rule read as that rule (YAML keeps the line breaks of `|` in one string, which
 * the reference installs as one entry no rule reads).
 *
 * Frontmatter is read by a deliberately closed subset reader (no YAML dependency — the runtime dependency budget is a
 * standing constraint): top-level key lines ({@link yamlKeyLineOf}), `#` comment lines, block scalars (`|` / `>` with their
 * indentation and chomping indicators), the tool lists, and nested blocks (a mapping or a sequence under a key with nothing
 * on its line) skipped wholesale but for a line that opens a tool-list key (below). A key other than the tool lists keeps its
 * value as written on its line (a value that is one quoted scalar, the scanner's own judgment, is that scalar decoded, over
 * every line it runs); a value of it that opens a quote, a `[` or a `{` and does not
 * close it refuses the skill too — the same scanner, over the same lines, says so. Every value takes its own lines and no
 * more, and every line no value takes is judged in one place: an indented `key:` line under a value that is not a nested
 * block — plain, quoted, a flow, a block scalar — refuses the skill naming both keys (YAML reads no key there), and a
 * flush-left line that is no key line refuses it naming the line (YAML reads no key there either). A tool list is read only
 * as a key of the frontmatter, written flush left: a tool-list key written in another key's nested block (at any depth), or
 * after another key's closing quote or `]` on its line, or as a key of a flow mapping (or a pair in a flow sequence) that
 * value holds, refuses the skill naming both keys — its list would be lost.
 */
import type { SkillSpec } from "./types.js";
/** Why one skill directory (or one of its parts) was not taken as-declared. */
export type SkillsDirectoryWarningCode = 
/** The directory holds no `SKILL.md`. */
"no_skill_file"
/** `SKILL.md` has no well-formed `---` fenced frontmatter block: no opening fence, an opening fence never closed by a whole-line `---`, a tool list (`allowed-tools` / `disallowed-tools`) whose value the reader cannot read as a list, a key whose value opens a quote, a `[` or a `{` it does not close, a value that is not a nested block followed by an indented `key:` line, a tool-list key written anywhere but flush left (in a nested block, or after a closing quote or `]` on a key's line), or a flush-left line that is no key line. */
 | "no_frontmatter"
/** Required `name` absent. */
 | "missing_name"
/** `name` breaks the charset/length rule. */
 | "invalid_name"
/** `name` disagrees with the directory name (the spec requires they match). */
 | "name_mismatch"
/** Required `description` absent. */
 | "missing_description"
/** `description` exceeds the spec cap. */
 | "description_too_long"
/** An attachment was left out (non-text bytes, over budget, unreadable, or resolving outside the skill). */
 | "attachment_skipped"
/** A file or directory entry existed but could not be read/resolved. */
 | "read_failed"
/** A skill directory or its `SKILL.md` resolves outside {@link SkillsDirectoryOptions.containWithin} — skipped. */
 | "outside_root";
/** One non-fatal diagnostic from a load. Safe to log: no file contents, only names/reasons. */
export interface SkillsDirectoryWarning {
    code: SkillsDirectoryWarningCode;
    /** Directory name of the skill this concerns, relative to the scanned root. */
    skill: string;
    /** Human-readable detail (names the offending field/file and what was done about it). */
    detail: string;
}
export interface SkillsDirectoryOptions {
    /** Sink for non-fatal diagnostics. Absent ⇒ warnings are dropped and the load still proceeds. */
    onWarning?: (warning: SkillsDirectoryWarning) => void;
    /**
     * Total decoded-text budget for ONE skill's attachments, in bytes (default 256 KiB). Files are taken in
     * path order until the budget is spent; the rest are skipped with a disclosed warning. Attachments also
     * ride a separate delivery cap at invoke time, so a generous value here does not enlarge the prompt.
     */
    maxAttachmentBytes?: number;
    /**
     * The directory this root's skills must physically lie inside. Declared ⇒ every skill directory, and every
     * `SKILL.md`, resolves (links followed) inside it or is skipped with an `outside_root` warning; the attachments
     * already lie inside their skill directory, so they are inside it too. Absent ⇒ each root entry is a mount the
     * deployment declared, trusted like the root itself. A root a third party writes (a repository's skills
     * directory) declares the repository root here, so a link in it cannot load a skill from elsewhere on disk.
     * Must name an existing directory; anything else throws, like an unreadable root.
     */
    containWithin?: string;
}
/**
 * Load every `SKILL.md`-bearing subdirectory of `dir` into a {@link SkillSpec}, ready for
 * `TaskSpec.skills`. Results are ordered by directory name (deterministic across filesystems).
 *
 * Per-skill problems are NON-FATAL: a directory whose frontmatter breaks the spec (missing/invalid
 * `name`, missing/over-long `description`, no fenced block, no `SKILL.md`) is SKIPPED and reported
 * through {@link SkillsDirectoryOptions.onWarning}, so one malformed skill cannot take down a whole
 * library. The root itself is different — an unreadable/non-directory root THROWS, because silently
 * returning `[]` for a mistyped path would look exactly like "this deployment has no skills".
 */
export declare function createSkillsFromDirectory(dir: string, options?: SkillsDirectoryOptions): SkillSpec[];
