Skip to content

[Feature]: skills install / skills sync command to install skills from lock file #283

Description

@lunelson

Problem

Hey there, first off thanks for building skills — it’s a really nice abstraction!

I’ve been digging into the lock file behavior, hoping it might offer something like a “reproducible state” mechanism across machines.

Current behavior (as I understand it)

  • The global lock file lives at ~/.agents/.skill-lock.json and has the shape defined in src/skill-lock.ts (and redefined in src/cli.ts):

    • skills: Record<string, SkillLockEntry> keyed by skill name
    • Each SkillLockEntry stores:
      • source (normalized identifier, e.g. owner/repo)
      • sourceType (github, mintlify, huggingface, local, …)
      • sourceUrl (the URL actually used with skills add)
      • optional skillPath
      • skillFolderHash (GitHub tree SHA for the skill folder)
      • installedAt / updatedAt timestamps
  • Global installs (via runAdd in src/add.ts) update the lock file by calling addSkillToLock, which writes/updates an entry for the installed skill.

  • skills check (runCheck in src/cli.ts):

    • Reads the lock file.
    • Builds a CheckUpdatesRequest using only entries that have a skillFolderHash.
    • POSTs to https://add-skill.vercel.sh/check-updates.
    • Prints which skills have updates available, but does not install or modify anything.
  • skills update (runUpdate in src/cli.ts):

    • Does the same lock-file → CheckUpdatesRequest → API call.
    • If there are updates, it reinstalls only those updated skills, using the sourceUrl and skill name from the lock entry:
      • Roughly: npx -y skills <entry.sourceUrl> --skill <name> -g -y.
    • It does not attempt to install all skills in the lock file, only those whose skillFolderHash differs from the server’s latestHash.
  • Listing actual installed skills is done via the filesystem (e.g. .agents/skills/<skill-name> and agent-specific directories in installer.ts / list.ts), not via the lock file.

So the lock file is currently acting as a global registry + update-tracking database, rather than a declarative “install manifest” like package.json or a fully deterministic package-lock.json.

Pain point

Because the lock file is global and already contains:

  • the skill names,
  • the original sourceUrl used to install,
  • and the provider/path information,

it feels like the perfect place to drive reproducible installs across machines or after deletion.

However, in practice:

  • If I sync only ~/.agents/.skill-lock.json via dotfiles onto a new machine:

    • skills check and skills update on that machine can now see those skills and their hashes.
    • But skills update will only reinstall skills that the API considers “out of date”. If the stored skillFolderHash already matches the remote hash, that skill is not included in updates, so it does not get reinstalled—even if it’s completely missing on disk on that machine.
  • There is currently no command that says “install everything from this lock file”.

    • So I can’t use the lock file as a true manifest for:
      • setting up a new dev machine, or
      • restoring skills after a cleanup, or
      • keeping multiple machines in sync using just dotfiles.

Right now, I’d need to manually re-run npx skills add ... for each repository, or write my own script around the lock file structure.

Proposed Solution

I’d love a first‑class command, something like:

  • skills install (or skills sync, skills restore, etc.)

that:

  1. Reads ~/.agents/.skill-lock.json (and/or a project-local lockfile, if that ever exists).

  2. For each entry in lock.skills:

    • Uses entry.sourceUrl and name to reinstall that skill via the existing CLI pipeline, e.g.:

      # Conceptually
      npx skills <entry.sourceUrl> --skill <name> -g -y
  3. Optionally supports some modes/flags, for example:

    • --missing-only: only install skills that are not currently present on disk (according to installer.ts / listInstalledSkills).
    • --all: reinstall everything in the lock file regardless of hash (useful for a completely fresh machine).
    • --dry-run: show what would be installed/reinstalled, using lock entries as the source of truth.

This would provide:

  • A reproducible global skill set across machines by simply:
    • syncing ~/.agents/.skill-lock.json via dotfiles, and
    • running npx skills install on a new machine.
  • A way to recover from manual deletions of skill directories without having to remember all original skills add commands.

And this fits the design because:

  • The lock file already holds everything needed to reconstruct the install command:
    • sourceUrl (exact URL originally used),
    • source / sourceType / skillPath (for future flexibility),
    • and the skill’s logical name (the key in lock.skills).
  • skills update already uses this data to drive a reinstall step for updated skills.
    • skills install/sync would just generalize this from “only updated skills” to “all skills (or all missing skills) in the lock file”.
  • It keeps the mental model cohesive:
    • skills add: install new skills and write them to the lock file.
    • skills check: read lock file to detect which tracked skills could be updated.
    • skills update: read lock file + update only out-of-date skills by reinstalling.
    • skills install / skills sync: read lock file + ensure all tracked skills are present on the local machine.

Alternatives Considered

No response

Additional Context

Possible open questions

Some questions I’m not presuming to answer, but worth considering:

  • Should skills install:
    • only operate on global skills (current lock file), or
    • support a future project-level lock file as well?
  • How should it behave if:
    • the lock file references a skill whose repo is now gone or private?
    • sourceUrl has changed or is invalid?
  • Should it integrate with listInstalledSkills to detect “missing” skills more robustly?

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions