Skip to content

How It Works: Store, Scopes, Links, Ledgers

A scope is one skills directory (user-global ~/.claude/skills/ or a project's .claude/skills/). The project directory is the environment — cd is activation. IpMan's job is organization: one stored original per skill, symlinks wherever it's used, ledgers that can rebuild everything.

The data flow

ip.yaml ──resolve──> central store clone ──pin──> @<commit> worktree
                                                        │
ip.lock <──record── commit + tree hash                  └──symlink── .claude/skills/<name>

The two-layer store

~/.ipman/store/<source-id>/repo/        # live git clone
~/.ipman/store/<source-id>/@<hash12>/   # immutable worktree per pinned commit
  • repo/ is an ordinary, editable git clone. This is where --live links point, where ipman update fetches, and where your bug fixes become commits you can PR upstream. With --adopt, repo/ is a symlink to a clone you already had (e.g. ~/GitHub/...) — ipman never copies or deletes it.
  • @<hash12>/ directories are detached git worktrees, created on demand and locked against git worktree prune. They are treated as immutable: two projects can pin the same skill at different commits and coexist, because each pin gets its own worktree.

Read-only snapshots guard the store

Pinned @<commit> worktrees are made read-only. Absorb defaults to pinned for exactly this reason: a stray rm -rf some/link/ with a trailing slash follows the symlink and tries to empty the target — but a read-only snapshot can't be gutted, so the store survives. Use --live only for skills you're actively editing.

<source-id> is the normalized URL (github.com/owner/repo) for remote sources, or a short hash of the absolute path for local ones. Override the store location with IPMAN_STORE_ROOT.

One symlink per skill, directly inside each agent's skills directory:

.claude/skills/<name>  ->  ~/.ipman/store/<id>/@<commit>/<skill-dir>   # pinned
.claude/skills/<name>  ->  ~/.ipman/store/<id>/repo/<skill-dir>        # live
  • Managed links coexist with your own real skill directories — ipman never overwrites a real directory, and ipman doctor reports them as unmanaged.
  • Managed links are git-ignored via a marked block in .gitignore; ip.yaml and ip.lock are what you commit.

pinned vs live

pinned (default) live (--live)
Link target immutable @<commit> worktree editable repo/ working copy
Reproducible exactly, from ip.lock best-effort (fresh machines check out the locked commit)
Updates explicit, per-project (ipman update) immediate, in every live-linked project
Use for consuming skills developing / contributing to skills

Custody (接管目录)

Where absorb protects one skill at a time, custody protects a scope's whole skills directory — the right tool for a suite root that other skills link into (see the dependent-detection guard).

ipman takeover user          # or . for the current project
ipman takeover .  --from-link # migrate an already-symlinked dir into custody
ipman release user           # hand it back (contents restored in place)

takeover moves a scope's whole skills directory into ~/.ipman/scopes/<id>/content/ and replaces the path with a directory-level symlink — created once, never repointed. The agent notices nothing (verified by chain canaries: registration and invocation work through dir-link → skill-link → read-only snapshot). Value: the scope becomes survivable — custody link destroyed? ipman sync rebuilds it; store originals are read-only snapshots a stray rm -rf cannot gut. release reverses it.

Repair. If an external updater recreates the real directory and clobbers the custody link, just run ipman takeover <scope> again: it auto-repairs the link and merges any new skills that appeared in the recreated directory into custody.

Ledgers

Two ledgers make everything above rebuildable:

  • ip.yaml + ip.lock live in each project scope (see the data flow above): ip.yaml is your declaration, ip.lock the exact commit + tree-hash pin.
  • ~/.ipman/scopes/<id>/scope.yaml records a scope's custody state and, per skill, whether it's active or archived — this is what enable/disable read and write, and what lets enable restore precisely the version you had.

ipman sync replays these to recreate every link (including a lost directory- level custody link) exactly as recorded.

Retired: ipman env and the prompt tag

The old virtualenv-style surface (env create/activate + [ip:name] shell prompt tag) is gone: activation needed source-based shell integration and couldn't express multi-scope state. Packs replace exclusive environments (additive, overlapping, per-scope), and status display moved to the agent's status bar (ipman statusline).