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.
-
giton yourPATH. Loom shells out to the systemgitto fetch skills, exactly the way Homebrew does. -
A network connection.
install,update,test, andgenerateall reach out to the network. -
The GitHub CLI (
gh), for Part 8 only. Publishing forks a repo and opens a pull request throughgh. 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.
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.
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:
- The manifest was validated. A manifest with a hard error never gets as far as the network.
-
The source was fetched into a scratch directory
under
~/.loom/cache/build/— a shallowgit cloneat the pinned ref. -
The
subdirwas copied into~/.claude/skills/pdf/, and theentryfile (SKILL.md) was confirmed to exist. A missing entry file fails the install. -
The install was recorded in
~/.loom/state.json, which is howlist,upgrade, andremoveknow it is there.
Confirm with loom list:
loom list
output
pdf 0.1.0 [claude-code]
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.
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 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 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.
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.
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
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