Skip to Content
SkillsLayered sources

Layered sources

You can load skills from several folders at once, for example defaults from your platform, a user’s own skills, and a project’s skills. Pass a list of paths to skills=; when two sources define a skill with the same name, the later source wins. This lets a project override a shared skill without editing the shared copy.

from loomflow import Agent agent = Agent( "...", model="claude-opus-4-7", skills=[ "~/.loomflow/skills/system/", # base layer "~/.loomflow/skills/user/", # user customizes "./.loomflow-skills/", # project-local override ], )

If ~/.loomflow/skills/system/git-discipline/SKILL.md defines a git-discipline skill, and ./.loomflow-skills/git-discipline/SKILL.md also defines one with the same name, the project’s wins. The system version is shadowed entirely: body, bundled files, and any tools.

Labelled sources

Any source can be tagged with a human-friendly label by passing a (path, label) tuple. The label shows up in the skill catalog the model sees (- git-discipline [Project]: ...); tool prefixes stay the skill name:

agent = Agent( "...", model="claude-opus-4-7", skills=[ ("~/.loomflow/skills/system/", "System"), ("~/.loomflow/skills/user/", "User"), ("./.loomflow-skills/", "Project"), ], )

Auto-namespacing prevents collisions

Tool names get prefixed with the skill name automatically:

Skill shipsRegistered as
add (Mode C, in calc skill)calc__add
say_hi (Mode B, in greeter skill)greeter__say_hi
search (in two skills a and b)a__search and b__search (no clash)

The double underscore (__) is the namespace separator. Inside the skill, you write the un-prefixed name; the prefix is added at registration.

Source order matters

The framework loads sources left-to-right. For each source, every discovered skill (one directory per skill, plus inline Skill instances) is added to the registry. When a name collides with a previously-loaded skill, the new one replaces the old.

This means:

  • First-loaded source = lowest priority (gets overridden).
  • Last-loaded source = highest priority (wins on collision).

Standard convention:

  1. system/: defaults shipped with the framework or your platform.
  2. user/: what the operator customizes.
  3. project/: what the codebase pins.

Inline skills mix freely

You can combine path-based sources with inline Skill instances in the same list:

from loomflow import Agent from loomflow.skills import Skill agent = Agent( "...", model="claude-opus-4-7", skills=[ "~/.loomflow/skills/system/", Skill.from_text(""" --- name: tone description: Always reply in a brief, factual tone. --- Stay under 100 words. No filler. """), "./.loomflow-skills/", ], )

The inline skill obeys the same override rules. If ./.loomflow-skills/ also ships a tone skill, the project version wins.

Override is by NAME, not path. Two skills with different names in different sources both load. Two skills with the same name in different sources. The later source wins, the earlier is dropped.

Last updated on