Where should this skill live?

Skills end up on one machine, copied between repos, or stuck in dotfiles. Giving each skill a source, a selection, and a generated copy fixes all three.

Source, manifest, installed copy, at two levels Two rows of three boxes. The personal row goes from a skills repo on GitHub, through the global manifest, to the user-level skills directory. The project row goes from a skills directory inside the repo, through the committed project manifest, to the gitignored .claude directory. source manifest installed copy you github:you/skills ~/.config/skillfold/ ~/.claude/skills project ./skills/ ./skillfold.yaml .claude/skills
Edit the source, let the manifest pin it, never touch the installed copy. The same shape at two levels: yours, and the project's.

You wrote a skill and it works. Now it needs a home, and the obvious answers are all wrong in a few months. Drop it in ~/.claude/skills and it exists on one machine with no history. Commit it to the project's .claude/skills and a second project copies it, and the copies drift. Keep it in dotfiles as a local path and nobody else can install it, and neither can your CI.

Short answer: skills you use everywhere live in one GitHub repo of your own; skills for one project live in that project's skills/ directory. In both cases a manifest selects them and skillfold generates the copy the agent reads. So every skill has three homes: the source you edit, the manifest that selects and pins it, and the generated copy. Keep those three apart and the layout follows. The table gives the answer per case.

Where does this go?

You have Source Selected by
A skill for every machine github:you/skills/skills/<name> global manifest
A skill only this repo needs ./skills/<name> project manifest
A skill for a tool this repo depends on npm:<pkg>/<skill>@installed project manifest
A skill shared by several of your repos github:you/skills/skills/<name>@v1 (pinned to a tag) each repo's manifest
Someone else's skill its own home, unchanged whichever level needs it
A standing instruction, personal github:you/skills/rules/<name>.md global manifest (optionally limited to one agent or host)
A standing instruction, this repo ./rules/<name>.md project manifest

The global manifest installs into ~/.claude/skills and ~/.claude/rules; the project manifest into .claude/skills and .claude/rules inside the repo. Adding codex to the manifest's targets list also writes ~/.agents/skills and a generated block in ~/.codex/AGENTS.md (or .agents/skills and AGENTS.md for a project). The sections below explain each row.

Source, selection, installed copy

Source is where the skill is edited. It has a git history and a review process, and it is the only copy anyone changes.

Selection is the manifest and lockfile. The manifest says which skills and rules this level wants and from where; the lockfile pins each remote one to a commit and a content hash (a local ./ source is read as it is on disk, so it has no pin). Together they are the reproducible part: skillfold install --frozen turns them back into files on any machine.

The installed copy is generated. ~/.claude/skills, .claude/skills, ~/.agents/skills and the managed block in AGENTS.md are build output. Edit one and skillfold check reports the edit as drift; the next install replaces it with the pinned content.

Skillfold applies the same three-part layout at two levels, global and project, and keeps them independent. The global level (-g) manages your user-level directories from a manifest in ~/.config/skillfold/; the project level manages one repository from a manifest at its root. Nothing is merged between them, because the agents already read both levels at runtime. Two levels because personal rules, such as your communication style or notification channel, should not sit in a project manifest, where everyone who clones the repo would receive them.

Personal skills: one repo, selected from dotfiles

Skills you use everywhere belong in a repository of their own, pushed to GitHub, with the same shape a published skill set has:

you/skills
  skills/
    obsidian/SKILL.md
    tts/SKILL.md
    tts/scripts/speak.sh
  rules/
    communication-style.md
    hosts/
      work-laptop/telegram.md

Then the global manifest, kept in your dotfiles so it travels with the rest of your shell, points at that repo rather than at a path on disk. Third-party skills sit in the same manifest, pinned to their own repos:

# ~/.config/skillfold/skillfold.yaml
targets: [claude, codex]
skills:
  obsidian: github:you/skills/skills/obsidian
  tts:
    source: github:you/skills/skills/tts
    targets: [claude]
  frontend-design: github:anthropics/skills/skills/frontend-design
rules:
  communication-style: github:you/skills/rules/communication-style.md
  telegram:
    source: github:you/skills/rules/hosts/work-laptop/telegram.md
    hosts: [work-laptop]

Why a GitHub source rather than ./skills/obsidian inside dotfiles? A local source is never pinned, because you are editing it. A GitHub source gets three things a local one cannot:

  • A commit SHA and a content hash in the lockfile, so the lockfile is a statement of exactly which version every machine has.
  • Drift detection: skillfold check -g on any machine says whether its installed copy matches that lockfile.
  • A public address: a colleague can skillfold add github:you/skills/skills/obsidian without cloning your dotfiles (a private repo needs GITHUB_TOKEN set).

On a new machine the whole setup is the dotfiles checkout plus one command:

skillfold install -g --frozen

Editing a skill becomes a loop with a push in the middle: change it in the skills repo, push, then skillfold update -g obsidian to move the pin, skillfold check -g to confirm, and commit the lockfile in dotfiles. The other machine pulls dotfiles and runs install -g --frozen, and gets the pinned commit rather than whatever is on main by then.

Project skills: a directory in the repo

A skill that only makes sense inside one repository belongs in that repository, next to the code it describes and reviewed with it. This is the one case for a local source:

your-app
  skillfold.yaml          # committed
  skillfold.lock          # committed
  skills/
    release-checklist/SKILL.md
  rules/
    conventions.md
  .claude/
    settings.json         # committed, yours
    skills/               # gitignored, generated by install
    rules/                # gitignored, generated by install
# ./skillfold.yaml
targets: [claude]
skills:
  release-checklist: ./skills/release-checklist
  playwright-cli: npm:@playwright/cli/skills/playwright-cli@installed
rules:
  conventions: ./rules/conventions.md

Gitignoring the installed directories is a recommendation, not something skillfold init does for you. It is the same choice as ignoring node_modules: CI runs skillfold install --frozen to recreate them from the lockfile before any agent starts, and a fresh clone does the same. The rest of .claude/ (settings, hooks) stays yours and stays committed.

Two things do not belong in ./skills. Third-party skills come from their upstream repo or package and stay pinned there; copying one in means nobody notices when upstream fixes it. And a skill that documents a tool the project depends on should follow the installed version of that tool with @installed, so the next skillfold install after a dependency bump moves the skill with it.

Rules follow the same split

A rule is a single markdown file the agent loads on its own, with no task to trigger it. A skill is a procedure it reaches for when a task matches. The layout is the same as for skills. Personal rules live in the skills repo under rules/ and are selected from the global manifest; project conventions live in the project and are selected from its manifest.

Rules have one selector skills do not. targets works on both and restricts an entry to one agent, which is how a Codex-only compatibility note stays out of Claude Code. hosts is rules-only and restricts a rule to exact hostnames, so a rule about a notification channel that exists on one machine is still declared and pinned everywhere but installed only where it applies. Every machine shares one manifest and one lockfile; skillfold list -g marks the rules that are not selected on the current host.

What goes in a rule and what goes in a skill is a judgement about behaviour versus procedure. The test: if ignoring the instruction would be wrong in any session, it is a rule; if it only matters once a particular kind of task starts, it is a skill. "Never use em dashes" is a rule. "How to publish a release" is a skill. Keep rules short, because every rule is in every prompt and a long one is paid for on every turn. Keep facts out of them, because a rule that explains how a system works goes stale faster than a rule that says how to behave.

Three mistakes to avoid

  • Editing the installed copy. check flags it as drift and the next install replaces it. The edit belongs in the source.
  • The same name at both levels. The agent sees both, and which one wins is up to the agent (see below). Pick distinct names.
  • A source nothing else can fetch from. A local path in dotfiles is how one machine ends up with the only good version. A commit that was never pushed has a pin no other machine can resolve.

What this does not solve

Which copy wins when a project skill and a user skill share a name is the agent's behaviour, not skillfold's. Skillfold warns about skill collisions in project-mode check and list, never about rule collisions, and never resolves either. It does not know how each agent orders the two levels.

Reproducing a selection is not choosing one. Skillfold cannot tell a good skill from a bad one, and the rule-versus-skill judgement above is yours. check compares one machine's files to its lockfile; agreement between machines comes from both committing to the same lockfile, not from skillfold comparing them.

If you add cursor to targets: its user-level rules live in the app, not on disk, so a global manifest cannot install rules for it. Cursor rules work at the project level only.

The reference for the manifest keys used here is in docs/manifest.md; the two-level model is in docs/cli.md.