/**
 * A skill's BASE DIRECTORY: the placeholder grammar its material may use to name its own root, the bounded
 * substitution that resolves it, and the delivered-length measure the load gate budgets on.
 *
 * Split out of the skill tool because it is a different concern: the tool decides what a skill INVOKE
 * returns, this decides how the caller's material names files on the machine the tools run on.
 */
/** The typed refusal a `SkillSpec.baseDir` outside its domain raises — one code, one sentence. */
export declare const SKILL_BASE_DIR_INVALID_CODE = "config.skill_base_dir_invalid";
/**
 * The ONE screen of `SkillSpec.baseDir`: absent ⇒ no directory (nothing is substituted and no opening line is
 * added); a NON-EMPTY string ⇒ itself; anything else ⇒ a loud refusal carrying {@link SKILL_BASE_DIR_INVALID_CODE}.
 *
 * Both rejected shapes were SILENT before, and each was silent in its own direction. `""` is not "no directory":
 * it substitutes to nothing, so a body's `${SKILL_ROOT}/ref.md` is delivered as the filesystem-root path
 * `/ref.md` and the opening line names no directory at all — a wrong answer where absence would have given the
 * visible unresolved placeholder the seat's contract promises. A non-string makes the delivered LENGTH `NaN`,
 * and `NaN <= limit` is false, so the load gate dropped the skill and reported "content is NaN chars" —
 * a configuration typo reading as an over-limit body. Core still neither resolves nor reads the directory
 * (the brain-outside rule); this screens the VALUE, not the path.
 *
 * It runs at the load gate ({@link substitutedSkillLength}, which spec assembly calls for every skill), so the
 * substitution faces below are reached only with a value that already passed it.
 */
export declare function skillBaseDirOf(value: unknown): string | undefined;
/**
 * Substitute a skill's base directory for the root placeholders in ITS OWN MATERIAL.
 *
 * @contract skill.base_dir — with `SkillSpec.baseDir` set, a `Skill` result opens with the line
 * `Base directory for this skill: <baseDir>` and every `${SKILL_ROOT}` / `${CLAUDE_PLUGIN_ROOT}` in the
 * body AND in every attachment's content is replaced by that value; `files[].path` is then read relative to
 * it. The substitution covers the CALLER'S material only — never the model-supplied `args` section, which is
 * live input and is delivered as written. Core neither resolves nor reads the directory (the brain-outside
 * rule that keeps `SkillSpec.files` inline); with `baseDir` absent no line is added, nothing is substituted,
 * and the result is byte-identical to a pre-baseDir invoke.
 */
export declare function substituteSkillRoot(text: string, baseDir: string): string;
/**
 * Substitute under a CEILING, and report the size the full substitution would have had.
 *
 * Substitution is the one step here that can make text grow without bound: a placeholder is 13 or 21 chars and a real directory can be any length, so an
 * attachment that is nothing but placeholders expands by a factor of the path's length. Building that string in order to measure it against the attachments
 * cap — or worse, to discover it does not FIT and drop it — spends exactly the memory the cap exists to bound before the cap is consulted, and a big enough
 * one exceeds the runtime's own string limit. So the length is counted as the loop walks, nothing is materialized past `maxChars`, and the caller budgets on
 * `totalChars` while rendering only what fits.
 */
/** The DELIVERED length of a body once its root placeholders resolve — counted, never materialized, so the load gate measures what the model will actually
 *  receive rather than what the caller typed. Equal to `text.length` when no directory is stated, and the one place the seat's value is SCREENED
 *  ({@link skillBaseDirOf}) — the gate that budgets on this number is the first reader of every skill on every leg. */
export declare function substitutedSkillLength(text: string, baseDir: unknown): number;
/** The load gate's ONE reading: does a leg LOAD this skill — is the body it would deliver ({@link substitutedSkillLength}) within
 *  `limit`? Prepare's gate keeps the skills that load (announcing the rest), and a parked Skill approval's binding reads a
 *  skill that does not load as absent — so the binding and the activation judge one view. */
export declare function skillLoadReading(skill: {
    readonly content: string;
    readonly baseDir?: unknown;
}, limit: number): {
    readonly loads: boolean;
    readonly delivered: number;
};
export declare function substituteSkillRootBounded(text: string, baseDir: string, maxChars: number): {
    text: string;
    totalChars: number;
};
