Skip to content

Project and global scope

The scope decides where palm writes: project, the default, or global, chosen with -g. Both scopes use the same verbs, the same palm.yaml format and the same lock format.

~/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 install mattpocock/skills handoff -g
i ref ^1.2 saved to ~/.palm/palm.yaml (latest tag v1.2.3); edit ref: to track main
targets: claude (detected from ~/.claude/; change targets: in ~/.palm/palm.yaml)
+ skill handoff ~/.claude/skills/handoff/ 1 file from mattpocock/skills v1.2.3
1 installed.
$ palm get -g
kind name source ref sha targets files
───── ─────── ───────────────── ───────────── ─────── ─────── ─────
skill handoff mattpocock/skills ^1.2 → v1.2.3 2c0db90 claude 1

The first install wrote the skill into the project and declared the source in the project’s palm.yaml. The second wrote ~/.claude/skills/handoff/ and declared the source again, in ~/.palm/palm.yaml. Each scope has its own sources, entries, targets and lock.

You want Scope
The project needs it, and your team should get it too project
It in every project on your machine, such as a personal writing style skill global (-g)
The same personal setup on a second laptop global, with ~/.palm/palm.yaml and ~/.palm/palm.lock.yaml in your dotfiles

palm looks upward from the current directory.

  1. The nearest directory with a palm.yaml is the project root.
  2. Otherwise, the nearest directory with a .git is.
  3. Otherwise, the current directory is.

The search stops at the nearest .git, so a palm.yaml in another repository above yours is another project. When the root would be your home directory and it has no palm.yaml, palm refuses project scope and asks you to run inside a project or add -g. A dotfiles .git in your home does not count as a project.

palm also refuses project scope inside ~/.palm and inside a harness home, such as ~/.claude, ~/.codex or ~/.cursor. A palm.yaml whose real path is the global manifest, reached through a dotfiles symlink, is never a project root either. Each refusal names the same command with -g, and palm init follows the same rules.

palm init inside a folder of a project that already has a palm.yaml above it refuses, unless you pass --here. Monorepos explains why.

Project Global (-g)
Harness files under the project root, such as .claude/agents/ under the harness homes, such as ~/.claude/agents/
Manifest palm.yaml ~/.palm/palm.yaml
Lock palm.lock.yaml ~/.palm/palm.lock.yaml, with paths as tokens
What this machine holds git, because you commit the generated files ~/.palm/applied.yaml
Hook scripts .palm/assets/<source>/<entity>/, committed ~/.palm/assets/<source>/<entity>/
Sources declared in palm.yaml declared in ~/.palm/palm.yaml; project sources are invisible
palm create writes to ./agent-kit, or --in a folder in the same git worktree ~/.palm/kit, or --in a folder under your home
Paths in messages relative to the project root ~/...; tokens appear only in the lock
MCP secrets an environment reference; a literal only with --secrets literal an environment reference; a literal only outside every git worktree

The global lock never holds an absolute path. Each path starts with a token for a harness home, such as <claude>/skills/handoff/SKILL.md, so the same lock works on a machine where ~/.claude lives somewhere else.

palm keeps what it last wrote on this machine in ~/.palm/applied.yaml, with real paths and hashes. That file is the global counterpart of a git checkout, and you never commit it.

Keep in your dotfiles Holds
~/.palm/palm.yaml the global targets, sources and entries
~/.palm/palm.lock.yaml the resolved commits, render hashes and trust, with tokens for paths
~/.palm/kit/ your own skills, agents and rules, the in-repo source palm create -g writes to

Each of the three may be a symlink into the dotfiles checkout. palm writes through the links and keeps them links.

  1. On the first machine, set the global targets with palm init -g --target claude,codex, then install with -g.
  2. On the second machine, check out the dotfiles and run palm install -g.
  3. After a pull that removed an entry, palm install -g --dry-run lists each file as would remove, and palm check -g reports applied files no longer in the lock. palm install -g deletes them.

When applied.yaml is lost, palm install -g rebuilds it. A file that matches the render is adopted, and a file that differs is reported and kept.

For the global scope, palm follows the variables the harnesses read.

Variable Replaces Token
CLAUDE_CONFIG_DIR ~/.claude <claude>
CODEX_HOME ~/.codex <codex>
COPILOT_HOME ~/.copilot <copilot>
GEMINI_CLI_HOME the home that holds .gemini, so ~/.gemini becomes $GEMINI_CLI_HOME/.gemini <gemini>
XDG_CONFIG_HOME ~/.config, so OpenCode’s ~/.config/opencode moves with it <opencode>
PALM_HOME ~/.palm <palm>

<home> stands for your home directory and <agents> for ~/.agents, and ~/.cursor is <cursor>. Some paths differ between the scopes. Claude Code keeps global MCP servers in ~/.claude.json, and Copilot keeps them in ~/.copilot/mcp-config.json. palm describe target <target> -g prints every global path for one harness.