vigiles API reference
    Preparing search index...

    Interface SkillSpec

    interface SkillSpec {
        _specType: "skill";
        argumentHint?: string;
        body?: string | InstructionFragment[];
        context?: "fork";
        description: string;
        disableModelInvocation?: boolean;
        disallowedTools?: readonly string[];
        inputs?: readonly SkillInput[];
        maxInlineCodeLines?: number;
        name: string;
        output?: OutputContract<
            Readonly<Record<string, OutputFieldType>>,
            Readonly<Record<string, OutputFieldType>>,
        >;
        postcondition?: Gate;
        purity?: AuthoredPurity;
        result?: Gate;
        steps?: readonly SkillStep[];
        tools?: readonly string[];
    }
    Index

    Properties

    _specType: "skill"
    argumentHint?: string

    Hint for the argument (frontmatter). Ignored when inputs is set — inputs derive the argument-hint instead.

    body?: string | InstructionFragment[]

    Freeform instruction body (linear/unstructured skills). Use this OR steps.

    context?: "fork"

    Execution context. "fork" runs the skill's body as the task inside a forked SUBAGENT (its own context window) instead of inline in the main conversation (Anthropic's context: fork frontmatter). This is the ONLY setting under which a skill gains a real call→return boundary — so it's the prerequisite for declaring an output Result contract (see output). Omit for the default inline execution.

    description: string

    Short description (used in frontmatter).

    disableModelInvocation?: boolean

    Whether to disable model invocation (frontmatter flag).

    disallowedTools?: readonly string[]

    Tools this skill may NEVER use — rendered to the disallowed-tools: frontmatter key (hyphenated for skills; a subagent's key is disallowedTools:, and they are read by different parsers, so the spelling is not interchangeable).

    🔴 This is NOT the symmetric twin of tools and the asymmetry is the whole reason it exists. allowed-tools: on a skill is a PRE-APPROVAL — it waives the permission prompt for what it lists and removes nothing from the pool, so a skill that declares only Read can still call Bash. disallowed-tools: is the only key measured to actually take a tool away. A skill without it inherits every tool the session grants, which is why audit reports each such skill as holding all three lethal-trifecta legs — including skills whose tools list looks tightly scoped.

    Entries are checked like a subagent's: a close typo of a real tool name blocks nothing and is reported, because a fence with a misspelled name reads as protection while granting everything.

    inputs?: readonly SkillInput[]

    Typed inputs — compile to argument-hint + a ## Arguments section.

    maxInlineCodeLines?: number

    Max lines for an inline fenced code block before compilation errors, forcing the script into a file referenced via file() (default 20). Keeps big scripts out of the skill body (token budget + progressive disclosure). Set 0 to disable.

    name: string

    Skill name (used in frontmatter).

    output?: OutputContract<
        Readonly<Record<string, OutputFieldType>>,
        Readonly<Record<string, OutputFieldType>>,
    >

    The skill's typed railway outcome — the SAME Result<ok, err> contract a subagent declares with result(okShape, errShape). Valid ONLY with context: "fork": a forked skill runs as a subagent, so it has the call→return boundary a typed outcome needs (compile errors if output is set without context: "fork"). When valid, compiles to a ## Output contract with a vigiles:ok / vigiles:err block — parseable (parseAgentResult) and testable (assertAgentOk) via the existing subagent rail. An INLINE skill has no return, so a typed outcome there is a category error — hence the gate. See research/spec-syntax-and-railway-scope.md.

    postcondition?: Gate

    Terminal postcondition — the skill is "done" only when this gate passes. Compiles to a ## Result section + a vigiles:result marker.

    🔴 NAMED postcondition, NOT result, because result already means something else one screen down: result builds a subagent's typed ok/err CONTRACT (and reaches a skill through output:). Two concepts under one word, told apart only by whether you wrote result: or output: result(...) — the doc comment on result had to spend a line saying "distinct from a skill's result: postcondition gate", which is the tell. A name that needs a disambiguating sentence is the wrong name.

    The compiled MARKER stays vigiles:result deliberately: it is the wire format between the compiler and parseSkillGates, and every already compiled SKILL.md on disk carries it. Renaming the authoring field is a source-level change; renaming the marker would invalidate stamps.

    Declare this skill's purity floor — compile rejects a tool contract looser than it. "pure" allows only read-only tools; "bounded" also allows decidable side-effecting tools (Write, Edit, …) but bars Bash / unknown-effect / inherits-all; "dangerously-unrestricted" (or omitting it) enforces nothing. NOTE: "pure"/"bounded" require an explicit read-only tools list — an absent list inherits ALL tools and is a violation.

    result?: Gate

    Renamed to SkillSpec.postcondition. Removed next major. Measured 2026-08-21: 0 of 46 specs in the consuming knowledge base set this field, so the window costs nothing and closes the collision above.

    steps?: readonly SkillStep[]

    Gated pipeline steps. When set, the skill compiles to a ## Steps checklist with a deterministic gate per step. Use this OR body.

    tools?: readonly string[]

    The allowed-tools contract for this skill. Each entry must be a known built-in tool or an MCP tool (mcp__server__tool). Omit to inherit all tools. When purity is "pure"/"bounded", the declared tools are checked against that floor — compile rejects a tool looser than the declared level.