Skip to content

Sources

A source is a git repository, optionally a folder inside it at a ref, or a directory inside your project’s git worktree. palm installs entities from sources, and every source is declared in palm.yaml, so it is a dependency of the project and never a per-user setting.

~/project
$ palm install mattpocock/skills tdd
i ref ^1.2 saved to palm.yaml (latest tag v1.2.3); edit ref: to track main
+ skill tdd .claude/skills/tdd/ 2 files from mattpocock/skills v1.2.3
1 installed. Commit palm.yaml, palm.lock.yaml and .claude/ together.
$ palm get sources
source alias kind ref sha/tree entries
───────────────── ───── ──── ───────────── ──────── ───────
mattpocock/skills git ^1.2 → v1.2.3 2c0db90 1
$ cat palm.yaml
targets: [claude]
sources:
mattpocock/skills:
ref: ^1.2
skills: [tdd]

The first install from mattpocock/skills declared it in palm.yaml with a version range. palm get sources shows each source with its range, the commit it resolved to and the number of entries taken from it.

The first word after palm install is the source.

You type palm declares Example
owner/repo a GitHub repository, keyed owner/repo palm install mattpocock/skills tdd
owner/repo/sub/dir the same repository with root: sub/dir palm install cursor/plugins/pstack comment-sicko
github:owner/repo the same as owner/repo palm install github:obra/superpowers --all
an https://, ssh:// or user@host:path URL a named source with url: palm install https://gitlab.acme.com/platform/agent-kit.git reviewer
any of these plus #ref the source with that ref: palm install mattpocock/skills#v1.2.3 tdd
./dir or ../dir an in-repo source, rendered from the working tree palm install ./agent-kit review
a name already in palm.yaml, or its alias that source palm install kit reviewer

palm also accepts file:// URLs, and http:// and git:// with a warning. It refuses every other scheme, ext:: style transports and a leading -. A directory may sit anywhere in the same git worktree, so a nested project can use the root’s kit as ../../agent-kit. A local path outside the worktree is refused, because a teammate’s clone would not have it.

A bare word is a source only when palm.yaml declares it. For any other word, the error suggests a command you can paste: ./<word> for a folder in the project, a declared source that offers that name, or the repository form. A repository that git ls-remote cannot reach prints repository not found or private, with the command to try yourself.

palm writes a source to palm.yaml the first time you install something from it. A repository you only list is fetched and indexed, and nothing is saved.

  • GitHub shorthand is keyed by owner/repo.
  • A URL is keyed by the repository name, or owner-repo when that name is taken. --as <name> picks the key yourself.
  • A directory is keyed by its project-relative path, such as ./agent-kit.
  • --layout <kind>=<glob>, repeatable, writes a layout descriptor with the new source.

Until a source is declared, every hint palm prints uses the form you typed, such as the URL or owner/repo, never a key that does not exist yet. A listing’s paste lines keep the #ref you typed.

palm always writes the ref: it used, so the intent is visible in review. A source you wrote into palm.yaml by hand without a ref: gets one on its first install, with the same line.

The repository has palm writes and prints
a #ref you typed that ref nothing extra
semver tags a caret range on the newest tag i ref ^1.2 saved to palm.yaml (latest tag v1.2.3)
no tags the default branch, by name i ref main saved to palm.yaml; edit ref: to pin a tag

Declaring the same URL and ref under another name prints ~ source acme → kit (renamed). The rename happens only after the install succeeds. --as <name> with a different #ref declares a second source for the same repository, so two skills can follow two refs. A different ref for a declared source prints ~ source acme: ref ^1.2 → main and needs a confirmation or --yes.

ref: is the one place a version lives. It is a tag, a branch, a commit or a semver range.

ref: Resolves to
v1.2.3 that tag
main the head of that branch when the source is first resolved or updated
3f2a1c9 that commit
^1.2 the highest tag from 1.2.0 up to, not including, 2.0.0
~1.2 the highest 1.2.x tag
1.x, v1 the highest 1.x tag, unless a branch or tag has exactly that name
">=1.2 <2" any range the semver package accepts

A branch or tag named exactly like the range wins, such as a 2.x maintenance branch or a moving v1 tag. Tags may carry a v prefix. Prereleases match only when the range names one.

The lock records the range, the tag it resolved to and the commit. A listing, a first install and palm update ask the remote for its current tags, so they never show a stale version. A bare palm install uses the locked commit. palm update moves the commit within the range, and palm update --to <ref> changes the range itself.

A ref: you edit by hand is an update too. The next bare palm install prints the update plan, asks for consent when a program changes, and needs --yes without a terminal.

A directory inside the project, such as ./agent-kit, is a source like any other. palm renders it from the working tree, so the directory is the truth.

  • The lock stores no commit for it. Each entry’s content hash covers that entry’s own files, so two pull requests that change two skills do not conflict in the lock.
  • A bare palm install re-renders every entry whose files changed and reports ~ re-rendered.
  • palm check fails for each entry whose files changed since the lock was written, and for a directory that is missing or renamed.
  • Hook scripts run in place, from the directory, so a script edit is live at once. palm hashes them, so after an edit the next install asks for consent again.
  • An in-repo source has no ref:. For a tagged checkout, use a file:// URL instead.

palm create writes templates into ./agent-kit and declares it for you. See Publish your own source.

The key is the source’s name everywhere: in palm.yaml, in the lock, in --allow-exec keys and in messages. A source may add an alias: to save typing.

palm.yaml
sources:
acme-kit:
url: https://gitlab.acme.com/platform/agent-kit.git
alias: kit
agents: [reviewer]

palm install kit tester then installs from acme-kit. Names and aliases are unique within one palm.yaml, ignoring case.

  • A source that overlaps an output directory, such as ./.claude. The error names both paths and suggests ./agent-kit.
  • An output directory that is a symlink into a declared source, such as .claude/skills -> ../skill. palm checks real paths on both sides, at listing and at install.
  • A source missing on disk, or a renamed in-repo directory, on every command until you fix palm.yaml.
  • A project source under -g. The error says acme is a project source; -g uses ~/.palm/palm.yaml sources only.
  • A second source for an entity of the same kind and name in one scope. E_CONFLICT names the owner and the palm remove command.

Any other symlinked output directory prints one notice per run, such as i .claude/skills is a symlink to ../shared; palm writes through it.

A marketplace or plugin manifest inside a source is only a hint for finding entities. It never registers anything, and palm never fetches another repository on its own. Scanning explains what palm reads.