For AI agents: the complete documentation index is available at https://scriptedalchemy.github.io/agent-bundle/llms.txt, the full documentation bundle is available at https://scriptedalchemy.github.io/agent-bundle/llms-full.txt, and this page is available as Markdown at https://scriptedalchemy.github.io/agent-bundle/guide/authoring/skills.md.
Skip to main content
  • English
  • Skills

    A Skill is a Markdown document plus the files it references. agent-bundle discovers Skills from the src/ convention, lowers each one into the spelling every selected host expects, and ships the directory's other files as that Skill's resources.

    The convention

    One directory per Skill, with SKILL.md inside:

    src/skills/release-review/
    ├── SKILL.md
    ├── assets/report-template.md
    └── references/checklist.md

    That ships with no declaration at all. Everything in the directory other than SKILL.md, and other than the rendered-skill source files, which are build inputs, becomes a resource of that Skill, copied into the artifact with its relative path preserved.

    The document

    SKILL.md starts with YAML frontmatter and continues as ordinary Markdown:

    ---
    name: release-review
    description: Review a release candidate and issue an evidence-based go/no-go verdict.
    ---
    # Release review
    
    ## Required resources
    
    - Read [the release checklist](references/checklist.md) to inspect the artifact.
    - Deliver the result with [the report template](assets/report-template.md).

    Frontmatter is required. A document with none reports AB3001; invalid YAML reports AB3002. name and description are what every host reads, the description is the activation surface, so write it as the condition under which the Skill should be used, not as a title.

    Links to sibling files are how a Skill points at its own resources. They stay relative in the emitted document, so they resolve inside whichever artifact the host installed.

    Keep instructions focused

    Host UIs load every Skill's name and description into context so the model can choose. Long or overlapping descriptions get truncated; vague triggers load the wrong Skill. Prefer a short capability plus a discriminating when, not a broad domain:

    Avoid, fires on anything database-shaped:

    description: Create and validate Postgres schema migrations. Use when working with databases, queries, models, or persistence.

    Prefer, fires when the task is actually a migration:

    description: Create and validate Postgres schema migrations. Use when adding or changing a migration, or reviewing its rollout.

    Put only useful shared instructions and real safety or authorization constraints in the body. A repeated “When to use” section is unnecessary when the description already gives the boundary.

    Treat the root SKILL.md as a router: link substantial mode-specific procedures from references/ and say when to read them (progressive disclosure). Do not load every reference at entry or create resource folders for a short, self-contained Skill. assets/ holds files used in the output, not instructions that must always enter context. Overly specific step-by-step itineraries that helped weaker models often over-constrain stronger ones, keep invariants, drop ceremony.

    Write the body so the agent matches the user's scope. A plan or review should not silently become implementation or release. Reuse existing authorization and current evidence. Complete the relevant preparation before a necessary approval, and finish with the requested result and its validation or limitations. Avoid mandatory broad audits, repeated checks, and fixed report rituals unless the workflow's actual contract requires them. A release-readiness verdict still requires current candidate checks; a planning task can identify those checks as remaining work.

    The create-agent-bundle minimal template follows this pattern. It inspects the current Skill tree instead of assuming the scaffold's original capabilities are unchanged. Apply the same principles to rendered Skills: review the emitted Markdown, not just the component source.

    Selecting Skills explicitly

    Conventional discovery covers src/skills/*/SKILL.{md,ts,tsx}. Declare skills when you need a different location, a subset, or a literal list:

    import { 
    const defineConfig: (config: AgentBundleConfig | ConfigFactory) => AgentBundleConfig | ConfigFactory
    defineConfig
    } from 'agent-bundle/config';
    export default
    function defineConfig(config: AgentBundleConfig | ConfigFactory): AgentBundleConfig | ConfigFactory
    defineConfig
    ({
    AgentBundleConfig.plugin: AgentBundlePluginConfig
    plugin
    : {
    AgentBundlePluginConfig.description?: string | undefined
    description
    : 'Engineering operations.',
    AgentBundlePluginConfig.name: string
    name
    : 'ops-bundle' },
    AgentBundleConfig.skills?: string[] | undefined
    skills
    : ['src/skills/*'],
    AgentBundleConfig.targets?: string[] | undefined
    targets
    : ['portable', 'codex', 'claude'],
    });

    Literal paths stay literal; globs match Skill directories or SKILL.md files. Config always wins over the convention.

    Host extensions

    Portable Skill metadata, name, description, license, compatibility, allowed-tools (the Agent Skills spelling; camelCase allowedTools is accepted only under targets.claude), and a free-form metadata record, is understood by every target. Host-specific keys live under their host so they reach only that adapter:

    HostExamples
    ampbuiltinTools, mcpServers (emitted as builtin-tools and mcpServers)
    claudeallowedTools, disallowedTools, argumentHint, model, effort, context: 'fork', background, userInvocable, disableModelInvocation, whenToUse, shell, paths, hooks
    codexdependencies.tools, interface (display name, icons, brand color, default prompt), policy.allowImplicitInvocation
    cursorglobs, icon, color, paths, disableModelInvocation

    targets in Skill frontmatter is that per-host object and nothing else; a list such as targets: ['claude'] is AB3006. A Skill is emitted to every target the project selects. There is no per-Skill artifact restriction.

    Claude Code, Codex, Cursor, and portable share skills/<name>/SKILL.md; Amp receives its copy under .amp/plugins/<plugin>/skills/<name>/SKILL.md. A host extension that lowers a shared document differently for one selected host than for another, targets: { claude: { effort: high } } in a claude + codex build, say, is a fatal AB4103 collision. Keep the emitted document identical for every selected host, or build those hosts into separate artifacts (one targets entry per build).

    Amp registration and skill MCP

    Amp does not scan a directory plugin's skills/ folder. Its generated index.js awaits amp.registerSkill({ path: 'skills/<name>' }) exactly once for every bundled skill. Amp lists the result as <plugin>:<skill>, so it does not compete with a bare skill discovered from the machine, project, Claude-compatible, built-in, personal, or workspace skill roots.

    builtin-tools gates tools registered by that same Amp plugin until the skill loads. mcpServers in SKILL.md frontmatter wins over a sibling mcp.json; an authored sibling is also preserved rather than overwritten. If canonical mcp.servers target Amp, the adapter emits the flat server map beside exactly one bundled skill only when neither native source exists.

    Path tokens

    Host placeholder syntax differs, ${CLAUDE_PLUGIN_ROOT} is not what Cursor or the portable standard write. Author the canonical token instead and let build-time lowering substitute the host spelling:

    TokenMeans
    agent-bundle:path:plugin-rootThe plugin install root.
    agent-bundle:path:plugin-dataThe host-provided plugin data directory.
    agent-bundle:path:workspace-rootThe user's workspace or project root.
    agent-bundle:token:argumentsThe invocation arguments.
    agent-bundle:token:session-identityThe current session identity.
    agent-bundle:token:skill-rootThe installed Skill's own directory.

    Lowering substitutes syntax only; no runtime value is resolved at build time. A token a host cannot express is reported rather than silently emitted as literal text.

    Rendered Skills

    When a Skill document is generated rather than typed, the same checklist repeated per environment, a table derived from a contract, put a component at src/skills/<name>/SKILL.tsx (or .ts). It default-exports a component and exports a frontmatter record; the build compiles the rendered tree into the SKILL.md document hosts consume:

    // src/skills/release-review/SKILL.tsx
    import { Skill } from 'agent-bundle';
    
    export const frontmatter = {
      name: 'release-review',
      description: 'Review a release candidate and issue an evidence-based go/no-go verdict.',
    };
    
    export default () => (
      <>
        <h1>Release review</h1>
        <p>Evidence lives under {Skill.PluginRoot()}/assets.</p>
      </>
    );

    Six Skill.* members emit canonical tokens: Skill.Arguments, Skill.PluginData, Skill.PluginRoot, Skill.ProjectRoot, Skill.SessionIdentity, and Skill.SkillRoot. Skill.Resource renders a Markdown link ([path](path)), not a token. Host syntax is applied during lowering, never in the component.

    A rendered source may import project code, so the document is computed from the same sources the plugin ships, and it may import agent-bundle/meta: the loader serves the same { name, packageName, packageVersion, version } the compiler stamps into every built surface, so a skill can print the version it documents. Its JSX compiles against the loader's own element factory rather than the project's react/jsx-runtime, the renderer walks plain element objects and needs no React internals, so the evaluation does not depend on how the process resolves react (the react-server condition of the route-unit pool included), and inspectWorkbenchSurface inspects a project with rendered skills from any pool.

    A hand-authored SKILL.md in the same directory always wins, an authored file beats a generated one, and the shadowed component reports the informational AB4735 nudge. A rendered module that fails to load reports AB3003; one that does not default-export a component function, or does not export a frontmatter record, reports AB3004.

    defineSkill types a Skill definition next to a rendered source:

    import { 
    const defineSkill: <Skill extends DefinedSkill>(skill: Skill) => Skill

    Identity helper so authors can type a Skill definition next to a rendered source.

    defineSkill
    } from 'agent-bundle';
    export const
    const skill: {
        description: string;
        name: string;
    }
    skill
    =
    defineSkill<{
        description: string;
        name: string;
    }>(skill: {
        description: string;
        name: string;
    }): {
        description: string;
        name: string;
    }

    Identity helper so authors can type a Skill definition next to a rendered source.

    defineSkill
    ({
    description: string
    description
    : 'Review a release candidate and issue an evidence-based go/no-go verdict.',
    name: string
    name
    : 'release-review',
    });

    Inspecting what shipped

    npx agent-bundle inspect --root . --skills

    The skill focus shows each discovered Skill, its provenance (conventional or config), its resources, and the per-target lowering decisions. In the developer Workbench, select the Skill under Application → Skills. Its emitted document is the default view; source/generated differences, frontmatter, resources, and eval coverage are inspector tabs.

    Raw HTML, JSX/MDX, and Mermaid inside Skill Markdown are inert in the Workbench renderer. That is a deliberate containment boundary, not a rendering gap.

    Next