Skip to content

Releases

v0.14.0

Choose a tag to compare

@github-actions github-actions released this 22 Oct 20:49

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 plugins copy-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-dir in 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
    as unknown.
  • Asked plugin consent at install, at a pin move that adds an entry, and at
    a write to project scope. With no terminal, --yes is the only answer.
  • Installed a whole plugin with capshelf add <url> --path <plugin-dir>.
    --list and 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 status the 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 show text output.
    Named the ignore rule that blocks a project-scope promote.
  • 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.json that holds a plugin row.
  • Upgrade: upgrade every machine that writes to the project first. Then run
    capshelf lock migrate once in each project and commit the project lock.
    Projects that track the bundled skill must then run
    capshelf update skills/capshelf.

v0.13.0

Choose a tag to compare

@github-actions github-actions released this 18 Sep 15:29

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 --list shaped 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 status group, 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 bare capshelf update moved no remote pin and listed the rows it left
    alone.
  • Added capshelf share <item> --adopt. It vendored the installed bytes into
    the shelf, recorded upstream, upstreamCommit, and upstreamPath in the
    item's .capshelf.yml, and released the previous owner. It also adopted a
    skill that skills.sh manages.
  • Refused a remote skill in promote, move, keep-local, revert, and a
    share without --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 status in a project with
    20 settings fragments took 289 ms instead of 1588 ms. apply, update, and
    add reuse 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 in status --diff for a remote
    skill, and discarded merge fields in update --merge.
  • Compatibility: capshelf add <something>/<name> now exits 3 when the first
    segment is not an item kind, where it exited 1. capshelf share now exits 3
    instead of 6 when a project has no data-repo binding and the share also hits
    an earlier refusal. status --strict exits 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/capshelf after the upgrade. The first
    capshelf add <url> in a project appends remotes.lock.json to the
    committed .capshelf/.gitignore. Review and commit that line.

v0.12.0

Choose a tag to compare

@github-actions github-actions released this 08 Sep 07:23

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 functions status runs. 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.
    init and ui registered 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, so status --strict exits 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 install on 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'. Use update <item> --merge, review the result,
    then run a normal promote.
  • Upgrade: version 4 locks stayed compatible. No migration was required.
    Projects that track the bundled skill must run
    capshelf update skills/capshelf after the upgrade. Run capshelf ui once
    in each older project to register it for the dashboard.

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 03 Sep 17:22

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 init and to add with no item.
  • Added project scanning to share with no item. It found unmanaged config,
    skills, Pi extensions, and subagents.
  • Added the tracked-item picker to promote with no item. Ctrl-V previewed
    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 during share.
  • Compatibility: changed terminal-based init to open the picker. Use
    --no-pick when 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/capshelf after the upgrade.

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 22 Aug 20:06

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 --json call 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. Added view, from, and to to each --json diff entry. A comparison that capshelf cannot read has text: null and an unavailableReason.
  • 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 --merge now refuses a skills.sh-owned skill before it plans, and again before it saves.
  • Stopped a missing data-repo clone from blocking every command. status gives 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 apply to write all fragment outputs as one unit, as update, rm, and revert do. 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, and update at status <item> --diff-view installed.
  • Added an end-to-end suite that drives the compiled binary: bun run e2e, or bun run e2e:run against CAPSHELF_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, in package.json#packageManager.

Compatibility

  • status --diff prints two comparisons, and new header text. A script that reads the human output for diff data/<kind>/<name> or +++ <file> (current) finds no match. --diff-view installed gives the 0.9 comparison, but not the 0.9 headers.
  • A --json diff entry is no longer unique for each path. Use path and view together as the key.
  • A merge is not published until you promote it. The item reads as drifted until then, and capshelf status --strict exits 4.
  • make check no longer runs check-bun-pin or check-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/capshelf one time in each project, to take the new bundled skill.

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 17 Aug 12:38

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

  • add installs every fragment target the pinned commit holds.

    It used to pick targets from the data repo's working tree while apply picked
    them from the lock, and the cleanliness gate cannot see a deletion that
    removes a canonical path. An mcp item with codex.toml deleted in the
    worktree was therefore accepted, pinned to a commit that still contained the
    file, and installed with only .mcp.json written.

    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 apply with 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, and share: 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 targetCoverage to add, show, share, and every status --json row, carrying present: true | false | null plus coverageState when unknown; sources, dst, and the subagent targets array keep their existing shapes.
  • Cover target coverage in the bundled capshelf skill, which guided agents through the mcp and subagent workflows without mentioning the report those commands print.

Bug fixes

  • Fire the Codex project-trust warning only when an mcp item's locked commit has a Codex source; it used to fire for every mcp item, so a Claude-only item warned about the harness it does not reach and said nothing about the one it does not cover. codex-config items 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 blob with mode 120000, so filtering on object type reported it as a present source for an item capshelf refuses to pin outright.
  • Report every output rm reconciled instead of the first, so removing a two-source mcp item names .codex/config.toml as well as .mcp.json.
  • Fix ls --here rendering undefined in the identity column for data rows, and add --json and revert --json dropping the key entirely; ls --here --json gains lockedSha, the name status --json already uses.
  • Stop show reporting (update available) for every item forever, including immediately after a clean add; 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. status owns update detection and reported these items correctly all along.
  • Stop status exiting 1 for a whole report when one subagent's locked tree resolves but cannot be read.
  • Stop add re-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: --kind help omitted subagents, the lock description promised an inspect command, and the "data repo not found" error named a default path capshelf never reads.

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 15 Aug 19:04

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 migrate once 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, and apply keep 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 of git cannot do. With an older git on your machine,
    capshelf exits 7 and changes nothing.

  • Items are refused if the data repo runs them through git-lfs or
    git-crypt.

    Those tools do not put your file in the repository. They put a stand-in — a
    pointer file for git-lfs, ciphertext for git-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 — with sops or age, 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 +x in the data
    repo went unnoticed. It now shows as an available update in every project
    that tracks the item.

  • Your project's .gitignore no 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 add from the data repo commit's blobs, exactly as apply does, 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, and installation as independent axes in status --json, plus installDifferences and filteredPaths; the existing state field still carries the derived headline.
  • Prove promote publishes what the project holds by comparing the project snapshot against the tree the data repo commit produced, inside the transaction, so a pre-commit hook 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, text attributes, or eol=crlf, and deliver what Git stores — the same bytes every clone of that repository produces.
  • Render status --diff outside 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, while apply and revert keep 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 promote commit 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

Choose a tag to compare

@github-actions github-actions released this 10 Aug 07:34

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, and marketplace 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-local marker mean intent: only keep-local --unset clears it, revert keeps it, and promote refuses a marked item.
  • Recover an interrupted init by re-running it, and refuse a second init on an already-initialized machine (exit 3).
  • Remove a copy item with rm when the data repo clone is gone or its history was rewritten.
  • Make standalone add a 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.json and .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 --json error envelope without ANSI color, parse git status with -z, and let update re-pin a system item off superseded bundled content.
  • Upgrade warning: a --json or non-TTY run that used to overwrite local drift now exits 3 unless --yes authorizes the listed loss. Audit CI jobs and agent scripts before upgrading them.

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 02 Aug 20:04

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.yml as 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

Choose a tag to compare

@github-actions github-actions released this 16 Jul 18:07

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, and revert --local.
  • Preserve --local in stale-promote recovery guidance and warn users to copy local edits before updating.