Repository navigation
Releases
v0.14.0
Capshelf 0.14.0 added Claude Code plugins as a new item kind. A
plugins/<name> item copies a whole plugin directory into the folder that a
skill uses, from the shelf or from a repository URL. Capshelf listed the hooks
and the MCP servers that a plugin runs, and asked before it put them where
Claude Code runs them. The lock moved to version 5, so each project must
migrate once. Full examples are in
What's new in capshelf 0.14.
- Added the
pluginscopy-directory kind. It had the full skill lifecycle in
project scope and local scope, and bundles could include it. - Installed a plugin into the skill folder of its name. Claude Code loads it
as<name>@skills-dirin a trusted workspace. Codex sees the skills only. - Listed each hook and each MCP server from the pinned tree. Monitors, LSP
servers, unknown manifest keys, and unreadable declarations were reported
asunknown. - Asked plugin consent at install, at a pin move that adds an entry, and at
a write to project scope. With no terminal,--yesis the only answer. - Installed a whole plugin with
capshelf add <url> --path <plugin-dir>.
--listand the picker read plugin catalogs and grouped skills under their
plugins. - Refused a write that would give one skill folder a second owner. The check
compares folders with case and Unicode folding. - Reported in
statusthe hooks and MCP servers each plugin runs, and the
Claude Code workspace trust. Claude marketplace rows now print as
claude-plugins/<id>. - Stripped terminal control characters from
capshelf showtext output.
Named the ignore rule that blocks a project-scopepromote. - Compatibility: the project and local locks are version 5. Each command
that writes a lock refuses a version 4 lock with exit 3. Capshelf 0.13
refuses a version 5 lock, a manifest that holds a plugin, and a
remotes.lock.jsonthat holds a plugin row. - Upgrade: upgrade every machine that writes to the project first. Then run
capshelf lock migrateonce in each project and commit the project lock.
Projects that track the bundled skill must then run
capshelf update skills/capshelf.
v0.13.0
Capshelf 0.13.0 added remote skills: capshelf add <url> installs a skill
from any Git repository, and capshelf share <item> --adopt moves one into
your shelf. The record is gitignored, so a teammate who clones the project
gets nothing until you adopt it. Two commands reach the network that did not
before, and every other command stays offline. Status reports also got much
faster. Full examples are in
What's new in capshelf 0.13.
- Added
capshelf add <url>. It printed the repository, the commit, the
subpath, the file list, and the license finding, and then asked once.
--as,--ref,--path, and--listshaped the install. - Recorded each remote skill in
.capshelf/remotes.lock.json, schema version
1, which is gitignored. The install is local scope and the project lock is
untouched. - Added
capshelf status --check-upstream. It fetched every tracked
repository and reported which pins moved. It also re-created a missing
clone cache. - Reported remote skills in their own
statusgroup, with the upstream, the
ref, the pinned commit, and when it was last checked. - Moved a remote pin with
capshelf update <item>, offline, from the cache.
A barecapshelf updatemoved no remote pin and listed the rows it left
alone. - Added
capshelf share <item> --adopt. It vendored the installed bytes into
the shelf, recordedupstream,upstreamCommit, andupstreamPathin the
item's.capshelf.yml, and released the previous owner. It also adopted a
skill thatskills.shmanages. - Refused a remote skill in
promote,move,keep-local,revert, and a
sharewithout--adopt. Each refusal named the adopt. - Removed a remote skill and its record with
capshelf rm <item>. - Refused a repository URL that carried a credential, and stripped control
characters from repository-controlled text in the consent prompt. - Cut the Git work a status report does.
capshelf statusin a project with
20 settings fragments took 289 ms instead of 1588 ms.apply,update, and
addreuse reads the same way, within one command. - Refused an unreadable pinned fragment source before writing any output or
project metadata. The error names the canonical path and the commit. - Fixed a dropped second item in
capshelf update <a> <b>when two items
shared a name, a missing locked endpoint instatus --difffor a remote
skill, and discarded merge fields inupdate --merge. - Compatibility:
capshelf add <something>/<name>now exits 3 when the first
segment is not an item kind, where it exited 1.capshelf sharenow exits 3
instead of 6 when a project has no data-repo binding and the share also hits
an earlier refusal.status --strictexits 4 on a remote skill with no
clone cache on this machine, or one that left its upstream repository. - Upgrade: version 4 locks stayed compatible. No migration was required.
Projects that track the bundled skill must run
capshelf update skills/capshelfafter the upgrade. The first
capshelf add <url>in a project appendsremotes.lock.jsonto the
committed.capshelf/.gitignore. Review and commit that line.
v0.12.0
Capshelf 0.12.0 added a read-only web dashboard for every capshelf project on
the machine, plus the project registry that finds them. It also added a
shadow warning for Pi project skills, and it removed the deprecated
promote --merge option. Full examples are in
What's new in capshelf 0.12.
- Added
capshelf ui. It served the status of every registered project on
127.0.0.1, behind a token in the printed URL, and it wrote no file. - Showed each item's state, its commands, and the diff behind that state,
computed by the functionsstatusruns. External skills, Claude plugins,
user-level skills, and shadows appeared as read-only rows. - Added the project registry at
$XDG_CONFIG_HOME/capshelf/projects.json.
initanduiregistered the project. A registry failure warned and did
not fail the command. - Warned when a
.pi/skills/<name>entry hid a managed skill. The warning is
strict, sostatus --strictexits 4 while the shadow exists. - Worded a Codex user-level skill clash as
same name as, with no warning,
because Codex offers both skills. - Fixed an installed diff that hid a project-only file when the item was up to
date. Fixed leaked.capshelf-materialize-*directories after a busy
cleanup. - Fixed
make installon macOS. An in-place copy kept the old inode, and the
cached code-signature verdict made the kernel kill every run. - Compatibility: removed
promote --merge. The command now exits 1 with
unknown option '--merge'. Useupdate <item> --merge, review the result,
then run a normalpromote. - Upgrade: version 4 locks stayed compatible. No migration was required.
Projects that track the bundled skill must run
capshelf update skills/capshelfafter the upgrade. Runcapshelf uionce
in each older project to register it for the dashboard.
v0.11.0
Capshelf 0.11.0 added interactive pickers for setup, installation, sharing,
and promotion. It also added an inline publication preview and strengthened
checks for terminal input, hidden Git state, and changes to picker inputs
after selection. Full examples are in
What's new in capshelf 0.11.
- Added the shelf picker to
initand toaddwith no item. - Added project scanning to
sharewith no item. It found unmanaged config,
skills, Pi extensions, and subagents. - Added the tracked-item picker to
promotewith no item.Ctrl-Vpreviewed
the exact publication candidate before a commit. - Rechecked selected picker inputs before each write. A stale row failed
without blocking successful rows. - Detected Git index flags that hid canonical source changes from
git status. - Summarized binary diffs instead of printing raw bytes. Added specific
remedies to symlink refusals. - Fixed no-op fragment promotion, fragment conflict attribution, and legacy
lock checks duringshare. - Compatibility: changed terminal-based
initto open the picker. Use
--no-pickwhen automation must not prompt. Piped and JSON runs do not
prompt. - Upgrade: version 4 locks stayed compatible. No migration was required.
Projects that track the bundled skill must run
capshelf update skills/capshelfafter the upgrade.
v0.10.0
Capshelf 0.10 moves the merge out of promote and into update.
capshelf update <item> --merge merges your edit with the newer upstream
version. It writes the result to the installed copy, and moves the lock to the
upstream commit. It changes nothing in the data repo. You review the result,
then you publish it with an ordinary promote. status --diff supports that
review: it shows locked to installed, and locked to upstream. The diff also
shows changes that 0.9 dropped, such as an added empty file or a changed file
mode. This release adds the first end-to-end suite that drives the compiled
binary, and it fixes the three defects that the suite found. The lock format
stays at version 4, so 0.9 and 0.10 read the same locks and need no migration.
For full details, see docs/whats-new-0.10.md.
- Added
update <item> --merge. It is a three-way merge from the locked base into the installed copy. It supports skills and Pi extensions, in project scope and local scope. It takes one explicit item, refuses--yes, and needs a clean data repo and a base that capshelf can prove. A conflict writes nothing. - Deprecated
promote --merge, which merged and published in one step. It still works, and prints the replacement route on stderr. A--jsoncall does not change. - Added the second comparison to
status --diff. It now shows locked to installed, and locked to upstream. Added--diff-view installed|upstream|all, which also turns on--diff. Addedview,from, andtoto each--jsondiff entry. A comparison that capshelf cannot read hastext: nulland anunavailableReason. - Added existence and the executable bit to each side of the diff. An added empty file, a deleted file, and a changed file mode are now visible. 0.9 named these changes as drift in the row, then printed nothing.
- Added a collision check between destination paths and kept paths, before capshelf writes a reconciled tree. The check uses the folding that the destination performs.
update --mergenow refuses askills.sh-owned skill before it plans, and again before it saves. - Stopped a missing data-repo clone from blocking every command.
statusgives the report that it can, and exits 0. Commands that write exit 6 with the clone, bind, and retry steps. 0.9 exited 4, and named a repair in a directory that did not exist. - Changed
applyto write all fragment outputs as one unit, asupdate,rm, andrevertdo. A target that failed used to leave the earlier targets on disk. - Named every choice in the stale-promote refusal, in the order that you run them. Pointed the "review this first" messages in
apply,rm,revert, andupdateatstatus <item> --diff-view installed. - Added an end-to-end suite that drives the compiled binary:
bun run e2e, orbun run e2e:runagainstCAPSHELF_E2E_BIN. Added a release gate that needs a green Test run for the exact commit, and that refuses a tag that moved. Declared the Bun version one time, inpackage.json#packageManager.
Compatibility
status --diffprints two comparisons, and new header text. A script that reads the human output fordiff data/<kind>/<name>or+++ <file> (current)finds no match.--diff-view installedgives the 0.9 comparison, but not the 0.9 headers.- A
--jsondiff entry is no longer unique for each path. Usepathandviewtogether as the key. - A merge is not published until you promote it. The item reads as drifted until then, and
capshelf status --strictexits 4. make checkno longer runscheck-bun-pinorcheck-e2e-bin. Both scripts are gone, and their invariants are now enforced where they are used.- There is no lock migration. Run
capshelf update skills/capshelfone time in each project, to take the new bundled skill.
v0.9.0
Capshelf 0.9 makes every command that describes an mcp or subagents item
say which runtimes it covers. Either kind can carry a Claude source, a Codex
source, or both, and 0.8 printed one output path for all three cases, so a
Claude-only MCP server printed .mcp.json and said nothing about Codex. This
release also finishes moving add onto the pinned commit: fragments were the
last kind whose install target set came from the data repo's working tree while
apply took it from the lock. The lock format is unchanged — 0.8 and 0.9 read
each other's locks, and there is no migration; run capshelf update skills/capshelf once per project to pick up the refreshed bundled skill. Full
details in docs/whats-new-0.9.md.
Breaking changes
-
addinstalls every fragment target the pinned commit holds.It used to pick targets from the data repo's working tree while
applypicked
them from the lock, and the cleanliness gate cannot see a deletion that
removes a canonical path. Anmcpitem withcodex.tomldeleted in the
worktree was therefore accepted, pinned to a commit that still contained the
file, and installed with only.mcp.jsonwritten.A project that depended on that behavior will now get
.codex/config.toml
written where 0.8 wrote only.mcp.json. Commit the deletion in the data repo
if the target is genuinely retired. -
A canonical path committed as a directory or an empty tree is refused.
Both used to install, save a lock, and fail in a later
applywith a parser
error, leaving a broken lock already on disk. They are refused at pin time
now.
Enhancements
- Report runtime target coverage on
add,show,status, andshare: every candidate target marked present or absent, with the output path it feeds and the canonical source path a missing one reads, read at the pinned commit rather than the worktree. - Add
targetCoveragetoadd,show,share, and everystatus --jsonrow, carryingpresent: true | false | nullpluscoverageStatewhen unknown;sources,dst, and the subagenttargetsarray keep their existing shapes. - Cover target coverage in the bundled
capshelfskill, which guided agents through themcpand subagent workflows without mentioning the report those commands print.
Bug fixes
- Fire the Codex project-trust warning only when an
mcpitem's locked commit has a Codex source; it used to fire for everymcpitem, so a Claude-only item warned about the harness it does not reach and said nothing about the one it does not cover.codex-configitems keep the unconditional warning, and unknown coverage still warns — the gate removes a warning only on evidence. - Prove a pinned fragment source is readable before consent, so an unreadable object no longer plans as an empty contribution and locks as a covered target.
- Stop counting a committed symlink as a covered target; Git stores one as a
blobwith mode120000, so filtering on object type reported it as a present source for an item capshelf refuses to pin outright. - Report every output
rmreconciled instead of the first, so removing a two-sourcemcpitem names.codex/config.tomlas well as.mcp.json. - Fix
ls --hererenderingundefinedin the identity column for data rows, andadd --jsonandrevert --jsondropping the key entirely;ls --here --jsongainslockedSha, the namestatus --jsonalready uses. - Stop
showreporting(update available)for every item forever, including immediately after a cleanadd; it compared a content hash of the data repo's working tree against the locked pin digest, two schemes over two inputs that can never be equal.statusowns update detection and reported these items correctly all along. - Stop
statusexiting 1 for a whole report when one subagent's locked tree resolves but cannot be read. - Stop
addre-pinning after consent, so a data-repo commit landing in that window can no longer add a target the consent gate never saw. - Correct three user-facing strings:
--kindhelp omittedsubagents, thelockdescription promised an inspect command, and the "data repo not found" error named a default path capshelf never reads.
v0.8.0
Capshelf 0.8 fixes items that reported up-to-date forever while update,
apply, and revert all refused them. An item's identity is now the tree it was
pinned to in the data repo, rather than a hash of that repo's working copy, so
checkout filters, core.autocrlf, index bits, and sparse checkouts can no
longer change what a lock means. This is lock version 4: run capshelf lock migrate once per project, and note that Git 2.40.0 is now the minimum. Full
details in docs/whats-new-0.8.md.
Breaking changes
-
Run
capshelf lock migrateonce in every project.0.8 writes a new lock format, version 4. Capshelf 0.7 and older cannot read
it and refuse rather than guess, so order matters: upgrade capshelf
everywhere, including CI, before anyone commits a migrated lock. Until a
project migrates,status,ls,show, andapplykeep working, and any
command that writes the lock stops and tells you to migrate. The upgrade is
one-way. -
Capshelf now needs Git 2.40.0 or newer.
It has to read a file's Git attributes as of a particular commit, which
earlier versions ofgitcannot do. With an oldergiton your machine,
capshelf exits 7 and changes nothing. -
Items are refused if the data repo runs them through
git-lfsor
git-crypt.Those tools do not put your file in the repository. They put a stand-in — a
pointer file forgit-lfs, ciphertext forgit-crypt— and restore the real
bytes only when the tool runs during checkout. Capshelf gives each project
exactly what the commit holds, so it would hand them the stand-in. It now
refuses at the point where it would record the pin.The refusal is the same on every machine, including one where the tool is
installed and working, because capshelf reads the declaration from the data
repo's commit rather than from your local setup. To share a secret, commit it
already encrypted — withsopsorage, and no filter attribute. -
Changing a file's executable bit now counts as a change.
Before 0.8 the mode sat outside an item's identity, so
chmod +xin the data
repo went unnoticed. It now shows as an available update in every project
that tracks the item. -
Your project's
.gitignoreno longer changes what capshelf reports.Capshelf used to let the consuming project's Git decide whether an installed
file had drifted, so adding an ignore rule could change an item's status
without the file changing. Drift is now measured against the bytes on disk.
Project Git still decides what you may publish; it no longer decides what an
item is.
Enhancements
- Add
capshelf lock migrate, the only path to version 4: one transaction across the project and local locks, a legacy-hash audit that reports what it repaired, every blocker listed in one pass, plus--repin,--remove-item,--dry-run, and--json. - Materialize
addfrom the data repo commit's blobs, exactly asapplydoes, so the two routes cannot disagree about an item's content. - Name the kind of every install difference — content edit, line endings, encoding,
$Id$, mode, filter artifact — on the consent prompt, without letting the classification suppress it. - Report
pin,sourceState, andinstallationas independent axes instatus --json, plusinstallDifferencesandfilteredPaths; the existingstatefield still carries the derived headline. - Prove
promotepublishes what the project holds by comparing the project snapshot against the tree the data repo commit produced, inside the transaction, so apre-commithook or clean filter that rewrote the content unwinds the promotion.
Bug fixes
- Install and update items from a data repo that normalizes line endings with
core.autocrlf,textattributes, oreol=crlf, and deliver what Git stores — the same bytes every clone of that repository produces. - Render
status --diffoutside your machine's own Git configuration, so it no longer prints nothing for the line-ending difference being investigated. - Repair an unprovable pin with
capshelf update, whileapplyandrevertkeep refusing a target their locked commit cannot supply. - Stop one wedged item from aborting a whole-project
update; the failing item is reported and every healthy one is still written. - Run the data repo's own commit hooks on every
promotecommit path; the mechanism no longer depends on whether a Codex marketplace is configured, so a secret scanner or a commit-message check applies to the same command in every data repo.
v0.7.0
Capshelf 0.7 makes destructive reconciliation ask first: add, apply,
update, rm, revert, and Codex marketplace sync plan the whole operation,
list the local state a write would destroy, and require consent. Ignored local
files now survive reconciliation intact, keep-local records intent, and
interrupted or data-repo-less states are recoverable. The lockfile schema is
unchanged, so 0.6 and 0.7 interoperate.
- Preflight destructive changes across
add,apply,update,rm,revert, andmarketplace sync, with typed reasons, review commands, and one prompt per run. - Preserve ignored local-only files across a copy-item replacement with their real modes and symlinks, and treat executable mode as real drift.
- Make the
keep-localmarker mean intent: onlykeep-local --unsetclears it,revertkeeps it, andpromoterefuses a marked item. - Recover an interrupted
initby re-running it, and refuse a secondiniton an already-initialized machine (exit 3). - Remove a copy item with
rmwhen the data repo clone is gone or its history was rewritten. - Make standalone
adda byte- and lock-stable no-op for an installed item, and gate bundle installs through the same consent boundary. - Gate config comment loss for
.codex/config.toml, where comments are read, and announce the repairing rewrite for.claude/settings.jsonand.mcp.json, where they stop the file from loading. - Read an item's source tree once per command (an 11-file item: 210 git calls to 46), emit the
--jsonerror envelope without ANSI color, parsegit statuswith-z, and letupdatere-pin a system item off superseded bundled content. - Upgrade warning: a
--jsonor non-TTY run that used to overwrite local drift now exits 3 unless--yesauthorizes the listed loss. Audit CI jobs and agent scripts before upgrading them.
v0.6.0
Capshelf 0.6 adds cross-runtime subagents, plugin marketplace tooling, declared
runtime needs, clean stale-promote merging, and clone-local Pi extensions. It
writes lockfile version 3; upgrade every contributor and CI runner before
committing a lock written by 0.6.
- Manage one logical subagent with Claude Markdown and Codex TOML targets.
- Build, validate, sync, and package independent Claude/Cowork and Codex plugin catalogs from canonical skills.
- Declare and pin expected network hosts, environment variables, and commands in
.capshelf.ymlas runtime-neutral metadata. - Resolve supported stale promotes with an isolated three-way merge via
promote --merge. - Use project or clone-local scope for Pi extensions and compatible bundles.
- Reduce status-time blob loading, improve removal diagnostics, make the exported CLI entry point reentrant, and run tests in parallel.
- Upgrade before project writes: Capshelf 0.5 cannot read a lockfile after 0.6 saves it as version 3.
v0.5.1
Capshelf 0.5.1 fixes reconciliation and recovery guidance for clone-local
skills. Existing projects and data repos require no migration.
- Verify clone-local writes by hashing the materialized directory, fixing failures after
apply --local,update --local, andrevert --local. - Preserve
--localin stale-promote recovery guidance and warn users to copy local edits before updating.