Little documentation
Guides and APIAuthoring

Skills

Procedurally-disclosed knowledge that teaches agent roles how to handle specific situations.

Little Workflow: published on npm as little-workflow@0.2.0-alpha.1 (2026-10-06). Sections labeled UNRELEASED describe source-only changes. Release status

A Skill is a piece of procedural knowledge for an agent role — "how to design back-edge loops in LWIR", "how to validate output schemas", "candidate-review patterns from prior runs". Skills use progressive disclosure: the description is always in the system prompt (cheap); the full body is loaded on demand via bash when the agent decides it is relevant.

Skills are files the developer authors alongside their workflow. The SDK mounts them into the Harness session; the agent reads them when useful.

Related but distinct. Skills (like sessions and persistent dirs) are canonically a Harness concept — the SKILL.md format, progressive disclosure, and staging are defined by Little Harness. See the Little Harness skills docs for the format itself. This page covers only how Little Workflow scopes skills to the planner, orchestrator, worker, and fixer roles.

Format

Directory form (primary)

my-workflow/
└── skills/
    └── writing-loop-lwir/
        ├── SKILL.md             ← frontmatter + body
        ├── examples/
        │   ├── candidate-review.json
        │   └── document-review.json
        └── anti-patterns.md

SKILL.md is the entry point. Auxiliary files live alongside; the agent can cat them when relevant.

SKILL.md shape

---
name: writing-loop-lwir
description: How to design back-edge loops with decision steps for iterative refinement (worker → review → route).
---

# Writing Loop LWIR

Use a back-edge loop when the workflow needs iterative refinement...

## Pattern

The loop has three steps:
- A worker step that produces a candidate.
- A reviewer step that evaluates it.
- A decision step that either ends ("end") or routes back for another iteration.

## Common mistakes

- Forgetting maxVisits >= 2 on the worker step.
- Using steps.review.output instead of steps.review.lastOutput.

Single-file shorthand

my-workflow/
└── skills/
    └── output-schema-validation.md   ← frontmatter + body in one file

The file must start with YAML frontmatter:

---
name: output-schema-validation
description: How to validate ai.generate output against declared schemas.
---

# Output Schema Validation

[body]

Use single-file for simple skills. Directory form is preferred when the skill has examples or references.

Required frontmatter fields

FieldTypeNotes
namestringSlug; unique within a session's skill set. Matches the directory name or file basename.
descriptionstringOne-line summary. Auto-loaded into the system prompt at session start.

Optional: model (suggested quality tier hint; not enforced), allowed-tools (hint to the agent; not enforced).


The skill() factory

import { skill } from "@little-stack/workflow";

skill("./skills/writing-loop-lwir/")          // local directory
skill("./skills/output-schema-validation.md") // single file
skill("https://github.com/anthropics/skills", {
  skills: ["frontend-design"],
})                                           // Git URL — selected skill names

Signature:

export function skill(pathOrUrl: string, options?: RemoteSkillOptions): Skill;

The Skill is a lazy reference. The SDK resolves local paths and materializes remote Git skills in the role runtime before the session starts. Local skills can be a directory with SKILL.md or a single .md file.

Remote skills must use explicit Git URLs. GitHub URLs, GitLab tree URLs, generic HTTPS Git URLs, SSH URLs, and scp-style Git URLs are supported. Repository shorthands such as org/repo, github:org/repo, and gitlab:org/repo are rejected so the source and transport are unambiguous.

The skills option selects skill names from the remote repository, matching npx skills add URL --skill frontend-design. If skills is omitted, the runtime installs every non-internal skill discovered in the repository. To pin a version, include a commit SHA or ref in the Git URL or source options; the materialization cache is keyed by resolved commit SHA.

Remote Git skill loading is best-effort at runtime. If a remote repository cannot be fetched, a selected skill cannot be discovered, or a remote risk audit fails, the role session continues without that remote skill and records a warning on the harness session. Local skill path and metadata errors remain strict because they indicate a broken deployment artifact.

skill("https://github.com/anthropics/skills", {
  skills: ["frontend-design"],
});

skill("https://gitlab.com/acme/agent-skills/-/tree/main", {
  skills: ["customer-research"],
});

skill("git@github.com:acme/private-skills.git", {
  skills: ["deal-review"],
  auth: { type: "bearer", token: process.env.GIT_TOKEN },
});

Private Git sources can pass explicit auth with auth: { type: "bearer", token: process.env.GIT_TOKEN }. As a host-level fallback, set LITTLE_SKILLS_GIT_TOKEN and restrict where it is sent with LITTLE_SKILLS_GIT_TOKEN_HOSTS.

Risk gates are optional. If your deployment configures one, set skillOidcToken: process.env.VERCEL_OIDC_TOKEN so the runtime can call the Skills API. skillMaxRisk can be set globally, per role, or on a specific skill(...) source. For selected remote skills, skillRisk can assign per-skill policy inside that skill(...) call.


Loading semantics

At session start

The SDK reads all skill frontmatter for the session's configured skills (workflow-declared and org-mounted) and appends a skill list to the system prompt:

Available skills (read via `cat .agents/skills/<name>/SKILL.md`):
- writing-loop-lwir: How to design back-edge loops with decision steps for iterative refinement.
- output-schema-validation: How to validate ai.generate output against declared schemas.
- pii-handling: How to handle personally identifiable information in workflow inputs.

The description is in the system prompt; the body is not. The agent knows what skills exist and what they cover without paying tokens for the body upfront.

On-demand body loading

When the agent decides a skill is relevant, it reads the body via bash:

cat .agents/skills/writing-loop-lwir/SKILL.md

The output enters the next turn's context. Auxiliary files in a directory skill can also be read:

ls .agents/skills/writing-loop-lwir/
# SKILL.md  examples/  anti-patterns.md

cat .agents/skills/writing-loop-lwir/examples/candidate-review.json

No dedicated read_skill tool exists — bash is sufficient and keeps the tool surface minimal.

Mount path

.agents/skills/<name>/        ← materialized skill directory
.agents/skills/<name>/SKILL.md

Skills are staged read-only under .agents/skills/<name>/ from the role/root working directory. Directory-form and single-file local skills both materialize to this directory shape so the prompt can always point at SKILL.md.


Role applicability

Skills follow the model. Where a model runs in an agent loop, skills are useful. Where execution is direct (no model), skills are not relevant.

RoleSkills available
Plannerplanner.skills (workflow-declared) + org-mounted
Orchestratororchestrator.skills (run-declared) + org-mounted
Worker — ai.generate stepsworker.skills (workflow-declared) + org-mounted
Worker — code.run stepsNone (no model in the loop)
Worker — tool.call stepsNone (no model in the loop)
FixerInherits worker.skills + org-mounted

The Fixer inherits from worker.skills because it is fixing a failed worker step — the same domain knowledge applies. Per-step fixer skill overrides are post-alpha.

In the default worker harness an ai.generate step is one model call with no bash, so it sees the skill list (names and descriptions) but cannot read a skill body. Put instructions the step must follow in its with.system or with.prompt.


Declaring skills on roles

Skills are declared on each role's config, not as a top-level workflow field.

import { defineWorkflow, runOrchestration, skill } from "@little-stack/workflow";
import { openai } from "@ai-sdk/openai";

const candidateReview = defineWorkflow({
  id: "candidate.review",
  // ...
  planner: {
    model: openai("gpt-5"),
    system: "You design candidate-review workflows.",
    skills: [
      skill("./skills/writing-loop-lwir/"),
      skill("./skills/output-schema-validation.md"),
    ],
  },

  worker: {
    skills: [skill("./skills/ai-step-best-practices/")],
  },
});

// Orchestrator skills (deprecated surface) are declared on runOrchestration:
await runOrchestration({
  world,
  workflows: [candidateReview, candidateScoring],
  input,
  tools,
  orchestrator: {
    model: openai("gpt-5"),
    system: "Orchestrate candidate evaluation.",
    skills: [skill("./skills/batch-fan-out/")],
  },
});

Sources

Skills come from two sources, materialized before each role session starts:

1. Org-mounted skills

Files under dataDir/memory/org/skills/. Curated by an org admin, or written by a dreaming workflow that consolidates lessons from many runs. Available to all roles across all workflows in the same dataDir. Read-only.

2. Workflow-declared skills

Declared on the role config (planner.skills, worker.skills, orchestrator.skills). Local paths, or explicit Git URL references that the runtime materializes before the role session starts.

Run-time injection (a skills option on runWorkflow for all roles) is reserved for post-alpha.


Conflict resolution

Two skills with the same name from different sources:

Workflow-declared overrides org-mounted. If both declare name: output-schema-validation, the workflow's version wins. A warning is logged during skill materialization:

Skill 'output-schema-validation' from workflow overrides org-level skill of same name.

Same name within one source is an error. Two workflow-declared skills with the same name throw before the role session starts:

Error: Skill name 'loops' declared twice.

Example: planner system prompt assembly

Given the configuration above, the planner session sees:

[Developer system prompt]
You design candidate-review workflows.

[Role addendum — SDK-injected]
You are the Planner. Use bash to read memory and scratch. ...

[Skill list — SDK-injected]
Available skills (read via `cat .agents/skills/<name>/SKILL.md`):
- writing-loop-lwir: How to design back-edge loops with decision steps for iterative refinement.
- output-schema-validation: How to validate ai.generate output against declared schemas.
- pii-handling: How to handle personally identifiable information in workflow inputs.

[Memory/scratch mounts]
...

The planner's agent loop:

# Decide the loop pattern applies; load the skill:
cat .agents/skills/writing-loop-lwir/SKILL.md

# Browse examples:
ls .agents/skills/writing-loop-lwir/examples/
cat .agents/skills/writing-loop-lwir/examples/candidate-review.json

# Apply the pattern to draft the LWIR.

The body is read once per session. Future turns don't re-read — it is already in context.


Not in alpha

  • Run-time skill injection — a skills option on runWorkflow injecting for all roles. Alpha supports workflow-declared and org-mounted only.
  • Skill versioning — skill("foo@1.2.0") semver pinning.
  • read_skill(name) dedicated tool — bash is sufficient.
  • Dynamic skill discovery at runtime. The skill list is fixed at session start.
  • code.run access to skills — direct-execute steps have no model. If you need skill-driven code generation, that is the planner's job.
  • Dreaming-generated skills — a dreaming workflow (post-alpha) can consolidate run lessons into new org-level skills. The storage layer is ready; the dreaming workflow itself is post-alpha.

Cross-references

  • Memory — org skills live under dataDir/memory/org/skills/
  • Orchestrator — configuring orchestrator.skills
  • Workflow Config — workflow definition and planner config

On this page