Loom tutorial

Start with nothing installed. Finish with a skill running inside your agent, a manifest you wrote yourself, and a pull request opened against the repository. About 20 minutes end to end.

Every command below is real, and every block of output is what Loom actually prints. Work through the parts in order — each one builds on the last — or jump straight to Part 7 if you only came to author a skill.

What you need

  • A way to install a binary — Homebrew, the install script, or a Rust toolchain from rustup.rs if you would rather build from source. Part 1 covers all three.
  • git on your PATH. Loom shells out to the system git to fetch skills, exactly the way Homebrew does.
  • A network connection. install, update, test, and generate all reach out to the network.
  • The GitHub CLI (gh), for Part 8 only. Publishing forks a repo and opens a pull request through gh. Everything before Part 8 works without it.

The one idea to hold onto

A manifest is a recipe, not a payload. The file skills/pdf.yml in the Loom repository contains no PDF-handling instructions at all — it contains a URL, a git ref, and a sub-directory. The skill itself lives in someone else's repo, where its authors maintain it.

skills/pdf.yml            loom install pdf           ~/.claude/skills/pdf/
the recipe        ───▶   fetch + verify      ───▶   the payload
name, source URL,         clone at the pinned         SKILL.md and its
ref, subdir               ref, check the entry        helper files

Everything in this tutorial is a variation on that flow: reading recipes, running them, writing one, and sharing it.

Part 1 Install Loom

On macOS or Linux, Homebrew is the shortest path:

brew install wess/packages/loom

No Homebrew? The install script downloads the right prebuilt binary for your platform, checks its checksum, and puts it on your PATH:

curl -fsSL https://raw.githubusercontent.com/wess/loom/main/scripts/install.sh | sh

Or build it yourself, which is what you want if you plan to hack on Loom. This needs a Rust toolchain from rustup.rs:

cargo install --git https://github.com/wess/loom

The documentation also lists the prebuilt tarballs, if you would rather place the binary by hand.

Not cargo install loom That command does not install this tool — the loom name on crates.io belongs to an unrelated concurrency-testing library with no binary. This crate is named loom_ai, and installs a binary called loom. Use one of the routes above.

Verify the install

loom doctor is the fastest way to confirm the environment is sane. It checks the four things that break most often:

loom --version
loom doctor

output

 git available — git version 2.55.0
 loom home writable — /Users/you/.loom
 config loads — 3 agents configured
 repo at /Users/you/skills — manifest directory not found

! 1 issue(s) found

That last line is expected on a fresh install, and Part 2 fixes it. Loom has not downloaded the manifest repository yet, so it is looking for manifests in a skills/ folder next to wherever you happen to be standing.

While you are here, note where Loom keeps its own state. Every file it owns lives under one prefix:

~/.loom/
  config.json     which agents exist and where their skills folders are
  state.json      the registry of what you have installed, and where
  cache/          repository clone, downloads, scratch build dirs

Point LOOM_HOME somewhere else to relocate all of it at once. That is what the rest of this tutorial does not need, but it is exactly how you would try Loom without touching your real setup:

LOOM_HOME=/tmp/loom-sandbox loom doctor

Part 2 Sync the repository

Loom searches a local copy of the manifest repository, so the first real command you run is the one that fetches it:

loom update

output

==> Cloning repository from https://github.com/wess/loom
 Repository up to date

This clones the repository into ~/.loom/cache/repo and — because you have not overridden it — writes the resulting ~/.loom/cache/repo/skills path into config.json as your repo_path. Every later loom update becomes a fast-forward git pull instead of a fresh clone.

Run this first Until repo_path is set, Loom looks for manifests in ./skills relative to your current directory. Run loom search from your home folder before loom update and you will get manifest directory not found. That is the single most common first-run confusion.

Now doctor is clean:

 git available — git version 2.55.0
 loom home writable — /Users/you/.loom
 config loads — 3 agents configured
 repo at /Users/you/.loom/cache/repo/skills — 287 manifests

 everything looks good

Re-run loom update whenever you want the newest manifests. It only refreshes the recipes — it never touches a skill you already installed. That is loom upgrade's job, and you will meet it in Part 5.

Part 3 Install your first skill

You will install pdf, a skill that teaches an agent to read, fill, split, and generate PDF documents. Start by finding it.

Search

loom search pdf

output

NAME           VER    SOURCE         DESCRIPTION
pdf            0.1.0  anthropics     Use this skill whenever the user wants…
canvas-design  0.1.0  anthropics     Create beautiful visual art in .png and…
docx           0.1.0  anthropics     Use this skill whenever the user wants…
markitdown     0.1.0  K-Dense-AI     Convert files and office documents to…
  tip: `loom info <name>` for full details

Results are ranked, not merely filtered. An exact name match outweighs a name prefix, which outweighs a keyword hit, which outweighs a mention buried in the description — so the skill you meant is almost always the first row. The SOURCE column is the org that publishes the skill, which is worth a glance before you install anything.

Inspect before you install

loom info prints the whole manifest, so you can see precisely what will be fetched and from where:

loom info pdf

output

pdf 0.1.0
Use this skill whenever the user wants to do anything with PDF files…

  homepage: https://github.com/anthropics/skills
  authors: anthropics
  agents: claude-code
  source: Git https://github.com/anthropics/skills
  subdir: skills/pdf
  manifest: /Users/you/.loom/cache/repo/skills/pdf.yml

Read the source and subdir lines as the answer to "what code am I about to put on my machine?" Loom will clone that repository and copy that folder. Nothing else.

Install

loom install pdf

output

==> Installing pdf 0.1.0 for Claude Code
 Installed to /Users/you/.claude/skills/pdf

Four things happened, in this order:

  1. The manifest was validated. A manifest with a hard error never gets as far as the network.
  2. The source was fetched into a scratch directory under ~/.loom/cache/build/ — a shallow git clone at the pinned ref.
  3. The subdir was copied into ~/.claude/skills/pdf/, and the entry file (SKILL.md) was confirmed to exist. A missing entry file fails the install.
  4. The install was recorded in ~/.loom/state.json, which is how list, upgrade, and remove know it is there.

Confirm with loom list:

loom list

output

pdf  0.1.0  [claude-code]
Installs are idempotent Running loom install pdf again wipes ~/.claude/skills/pdf/ and lays it down fresh. That makes reinstalling a safe way to discard local edits — and a reason not to hand-edit files inside an installed skill.

Part 4 See what landed

Loom is done, but the interesting part is what your agent now sees. Look inside the folder:

ls ~/.claude/skills/pdf

output

forms.md    LICENSE.txt    reference.md    scripts    SKILL.md

SKILL.md is the entry file. Its YAML front matter tells the agent what the skill is for and when to reach for it; the body is the instructions. Everything beside it — forms.md, reference.md, the scripts/ folder — is loaded on demand by the agent as the skill directs.

Claude Code picks up skills from ~/.claude/skills at session start. Open a new session and ask it to do something PDF-shaped; it will find the skill on its own. There is no registration step and nothing to restart beyond the agent itself.

Nothing is hidden An installed skill is a plain folder of plain files. If an agent is not picking a skill up, cat the SKILL.md and read its front matter — the description field is what the agent matches against when deciding whether the skill is relevant.

Part 5 Upgrade and remove

Upgrade

loom update refreshed the recipes. loom upgrade acts on them: for every skill in your registry, it compares the version you installed against the version the repository now pins, and reinstalls the ones that moved.

loom update     # refresh the manifests
loom upgrade    # act on them

output

  pdf: up to date (0.1.0)
==> Upgrading docx 0.1.0 -> 0.2.0 [claude-code]
 1 skill(s) upgraded

Narrow it to one skill or one agent when you do not want to move everything at once:

loom upgrade pdf              # just this skill, every agent
loom upgrade --agent codex    # every skill, just this agent
Version strings, not commits Upgrade compares the manifest's version field against what you installed. Many manifests pin a moving ref like main while holding version steady — when that upstream branch advances, the version string does not, so upgrade reports "up to date". Force a refetch with loom install <skill>, which always reinstalls.

Remove

loom remove pdf

output

==> Removing pdf from Claude Code
 Removed /Users/you/.claude/skills/pdf

uninstall and rm are aliases for the same command. Removal is driven by the registry: Loom deletes the exact path it recorded at install time, so it will never wander into a directory it did not create.

Part 6 Work with other agents

Ask Loom which agents it knows about:

loom agents

output

* claude-code  Claude Code
    /Users/you/.claude/skills
  codex  OpenAI Codex
    /Users/you/.codex/skills
  cursor  Cursor
    /Users/you/.cursor/skills

* = default agent

The same skill can live in several agents at once. Each install is tracked separately, so you can upgrade or remove one without disturbing the others:

loom install pdf --agent codex
loom install pdf --agent cursor
loom list

output

pdf  0.1.0  [claude-code]
pdf  0.1.0  [codex]
pdf  0.1.0  [cursor]
Compatibility is advisory A manifest's compatibility list documents which agents the author tested. It does not restrict anything — the install target comes from --agent or your configured default_agent. Loom will happily install a claude-code skill into Cursor if you ask it to.

To teach Loom about an agent it has never heard of, add it to ~/.loom/config.json. The id becomes a valid --agent value immediately:

{
  "agents": {
    "claude-code": { "label": "Claude Code", "skills_dir": "~/.claude/skills" },
    "my-agent":    { "label": "My Agent",    "skills_dir": "~/.myagent/skills" }
  },
  "default_agent": "claude-code"
}

Part 7 Author your own skill

Time to build something. You are going to write a small skill, push it to your own repo, and describe it with a manifest that Loom can install.

Write the skill

A skill is a folder with a SKILL.md at its root. The YAML front matter is what an agent reads when deciding whether the skill applies; the body is what it reads once it decides.

mkdir -p my-skills/changelog
cd my-skills

changelog/SKILL.md

---
name: changelog
description: >-
  Write and maintain a CHANGELOG.md that follows Keep a Changelog.
  Use when the user asks to add a changelog entry, cut a release,
  or reorganize release notes.
keywords: [changelog, releases, semver]
---

# Changelog

## Adding an entry

Group entries under `Added`, `Changed`, `Deprecated`, `Removed`,
`Fixed`, and `Security`. New work goes under an `## [Unreleased]`
heading until it ships.

## Cutting a release

Rename `[Unreleased]` to the version and date, then open a fresh
`[Unreleased]` block above it.

Commit that, push it to GitHub, and tag it. The tag is what your manifest will pin, and pinning is what makes an install reproducible:

git init && git add . && git commit -m 'Add changelog skill'
git tag v0.1.0
git remote add origin https://github.com/you/my-skills
git push -u origin main --tags

Author the manifest

loom init walks you through every field, validating as you type, and can pre-fill its answers by inspecting the repo you just pushed:

loom init --url https://github.com/you/my-skills --ref v0.1.0 --out .

It reads your SKILL.md front matter for the name, description, and keywords; sniffs your LICENSE file for an SPDX id; and derives the author from the repo owner. Press Enter to accept each default. At the end it offers to test-fetch the manifest immediately.

--out . writes the manifest into the current directory. Without it, both loom init and loom new write into your configured repository folder — after loom update, that is the synced clone in ~/.loom/cache/, where a later update could overwrite it.

Needs a terminal loom init is interactive and refuses to run when stdin is not a TTY. In a script or a CI job, reach for loom new <name> or loom generate <url> instead.

Prefer to write YAML by hand? loom new drops a bare template and gets out of your way:

loom new changelog --out .

Either way, you end up with something like this:

changelog.yml

name: changelog
version: 0.1.0
description: Write and maintain a CHANGELOG.md that follows Keep a Changelog.
homepage: https://github.com/you/my-skills
license: MIT
authors:
  - You <you@example.com>
keywords: [changelog, releases, semver]
compatibility: [claude-code]
source:
  type: git
  url: https://github.com/you/my-skills
  ref: v0.1.0        # the tag you pushed
  subdir: changelog  # the folder holding SKILL.md
install:
  entry: SKILL.md
  files: []

The two fields that decide whether the install works are source.url and source.subdir. Together they must point at the directory that contains your SKILL.md. Get one wrong and the next step tells you.

Lint, then test

loom lint is offline and instant. It separates errors, which make a manifest uninstallable, from warnings, which are quality nits that never block anything:

loom lint changelog.yml

Give it a path and it lints that one file. Omit the path and it lints every manifest in the repository Loom is configured to read — which, once you have run loom update, is the synced copy under ~/.loom/cache/repo/skills, not whatever folder you are standing in.

output — a manifest with a nit and a blocker

 changelog.yml
    error homepage should be an http(s) url
    warn no authors listed
error: manifest has errors

Fix the errors and it goes green. Then prove the manifest actually resolves against the network. loom test fetches the payload into a scratch directory, confirms the entry file is really there, and throws it all away — your agents' skills folders are never touched:

loom test changelog.yml

output

==> Testing changelog 0.1.0
  fetched: 1 files
  entry: SKILL.md
 manifest resolves and entry file is present

If subdir is wrong you get source subdir not found. If the folder exists but has no SKILL.md you get entry file 'SKILL.md' not present in source. Both point straight at the field to fix.

Try it locally A manifest path works anywhere a skill name does. Install your draft before publishing it: loom install ./changelog.yml. Note that this is a path — Loom does not fetch a manifest from a URL.

Part 8 Publish it

Sharing a skill means adding your manifest to the repository's skills/ folder. Your skill payload stays in your repo, under your control — only the recipe is contributed.

loom publish automates the fork-branch-commit-PR dance through the GitHub CLI. It defaults to a dry run, so you can always look before you leap:

loom publish changelog.yml

output

==> Publishing changelog 0.1.0 to wess/loom
  → fork wess/loom to your account (if needed)
  → clone your fork and branch loom/changelog
  → copy changelog.yml to skills/changelog.yml
  → commit "Add changelog 0.1.0" and push
  → open a PR into wess/loom

warning: dry run — nothing was changed. re-run with --execute to open the PR.

Happy with the plan? Authenticate gh once, then run it for real:

gh auth login
loom publish changelog.yml --execute

Publishing re-validates the manifest first — a manifest with a hard error can never reach a pull request. Target a different repository with --repo if you run your own collection of skills.

Once the PR merges, a CI job rebuilds docs/skills.json and commits it back, so your skill appears on the Browse page without you lifting a finger. From that moment anyone can run:

loom update && loom install changelog
No gh? No problem Publishing is a convenience, not a requirement. Fork the repo, copy your .yml into skills/, and open the pull request by hand. The result is identical.

Part 9 Import a whole repo

Writing manifests one at a time is fine for one skill and tedious for twenty. loom generate clones a repository, finds every folder containing a SKILL.md, and drafts a manifest for each:

loom generate https://github.com/you/my-skills --ref v0.1.0

With no --out it prints the YAML to stdout so you can read it before anything touches your disk. Pass a directory to write the files:

loom generate https://github.com/you/my-skills --ref v0.1.0 --out skills/
loom lint skills/changelog.yml    # then lint each draft

output

==> Inspecting https://github.com/you/my-skills
 Discovered 3 skill(s)
  wrote: skills/changelog.yml
  wrote: skills/lint.yml
  wrote: skills/release.yml
  next: review each manifest, then `loom lint`

Generation infers as much as it can: the name, description, and keywords come from each SKILL.md's front matter, the license from your LICENSE file, and the author from the repo owner. A semver-looking --ref such as v1.2.0 becomes the version; a branch name such as main falls back to 0.1.0.

These are drafts, not finished work. Read every one before you publish it — the description that reads well inside a SKILL.md is often too long for a search result. create is an alias, if that verb suits you better.

Recap

The whole tutorial, as a single sequence:

# set up
brew install wess/packages/loom
loom doctor
loom update

# consume
loom search pdf
loom info pdf
loom install pdf
loom list
loom upgrade
loom remove pdf

# author
loom init --url https://github.com/you/my-skills --ref v0.1.0 --out .
loom lint changelog.yml
loom test changelog.yml
loom publish changelog.yml --execute

Where to next