npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@gleanwork/pluginpack

v0.8.0

Published

Compile one source of agent plugins — skills, commands, hooks, and MCP servers — into the native plugin format each AI app expects.

Readme

pluginpack

Experimental npm version CI License: MIT

One source of truth for agent plugins across AI app ecosystems.

pluginpack is a build tool for compiling portable skills, commands, agents, rules, hooks, assets, and metadata into the native plugin layouts expected by each AI app. It copies files, writes target manifests, and validates generated output; it is not a package manager or publisher.

Quick Start

Start with portable plugin components, declare the native targets you want, then run pluginpack build.

npm install -D @gleanwork/pluginpack

Create repo-level component directories:

skills/
  release-notes/
    SKILL.md
agents/
  search-assistant.md
commands/
  summarize.md
rules/
  style.mdc
hooks/
  before-run.sh
assets/
  icon.png
pluginpack.config.ts

Add a config that maps that portable source into native plugin outputs. source.skills gives the repo a simple portable install surface; sibling component directories are included when the selected target supports them or when you opt into them with components.

import { defineConfig } from "@gleanwork/pluginpack";

export default defineConfig({
  name: "acme-plugins",
  version: "0.1.0",
  source: {
    skills: "skills",
    rootPlugin: {
      id: "core",
      description: "Acme portable skills.",
    },
  },
  metadata: {
    description: "Acme agent plugins.",
    author: { name: "Acme" },
    license: "MIT",
  },
  targets: {
    cursor: {
      outDir: ".",
      plugins: {
        acme: {
          from: ["core"],
          path: "plugins/cursor/acme",
        },
      },
    },
    claude: {
      outDir: ".",
      pluginRoot: "plugins/claude",
      plugins: {
        acme: { from: ["core"] },
      },
    },
    antigravity: {
      outDir: "plugins/antigravity",
      plugins: {
        acme: { from: ["core"] },
      },
    },
    copilot: {
      outDir: "plugins/copilot",
      plugins: {
        acme: { from: ["core"] },
      },
    },
  },
});

Build and validate the generated outputs:

npx pluginpack build
npx pluginpack validate --target cursor

Users who only want portable skills install from the skills/ subpath, for example npx skills add owner/repo/skills --skill '*'. Claude, Cursor, Antigravity, and Copilot users install from the generated native layout that can include skills, agents, rules, hooks, assets, MCP config, and target-specific manifests.

Mental Model

Agent apps increasingly support similar ideas: skills, commands, agents, rules, hooks, MCP configuration, and plugin marketplaces. The packaging formats are different enough that maintaining one repo per app quickly drifts.

pluginpack does four things:

  • reads a portable source plugin from your repo
  • copies selected component directories into each target
  • writes the manifests each target expects
  • validates, diffs, prunes, and cleans generated output

It does not try to make every app behave the same. Target adapters own target-specific layout, manifests, and validation.

Recommended Shape

The preferred path is one public plugin repository with top-level component directories. skills/ remains the portable skills CLI install surface, while the other component directories feed native plugin outputs.

skills/
  release-notes/
    SKILL.md
agents/
  search-assistant.md
commands/
  summarize.md
rules/
  style.mdc
hooks/
  before-run.sh
assets/
  icon.png
pluginpack.config.ts

.cursor-plugin/
  marketplace.json
plugins/
  cursor/
    acme/
      .cursor-plugin/plugin.json
      agents/
      rules/
      hooks/
      skills/
  claude/
    acme/
      .claude-plugin/plugin.json
      agents/
      hooks/
      skills/
  antigravity/
    .pluginpack/
      antigravity.json
    acme/
      mcp_config.json
      plugin.json
      agents/
      rules/
      hooks/
      skills/
  copilot/
    .claude-plugin/
      marketplace.json
    .github/
      plugin/
        marketplace.json
    .pluginpack/
      copilot.json
    plugins/
      acme/
        agents/
        hooks/
        skills/
.claude-plugin/
  marketplace.json
.pluginpack/
  cursor.json
  claude.json

source.skills points at the repo-level skills directory and creates a root source plugin from the sibling component directories. source.rootPlugin.id creates the source plugin name used by each target's from array. The repo root is intentionally also home to generated native plugin outputs, so the skills/ subpath keeps skills CLI discovery focused on the canonical portable skills.

pluginpack writes a .pluginpack/<target>.json managed-file manifest for each built target. That manifest lets builds and cleanup commands remove stale generated files without touching source files or unmanaged repo content.

Components

In pluginpack, a component is a top-level plugin capability directory. Components are the portable pieces of a source plugin that may or may not exist in every target ecosystem.

Supported component directories are:

skills/
agents/
commands/
rules/
hooks/
scripts/
assets/
policies/
themes/

Target adapters translate those component directories into each app's native layout and manifest fields. Each target has a smart default component list. By default, claude, cursor, antigravity, and copilot emit skills and other native plugin support files but omit commands, since those ecosystems increasingly expose skills as slash commands.

Use components only when a plugin needs an exact target-specific component set:

import { defineConfig } from "@gleanwork/pluginpack";

export default defineConfig({
  name: "acme-plugins",
  version: "0.1.0",
  source: {
    skills: "skills",
    rootPlugin: {
      id: "core",
      description: "Acme portable skills.",
    },
  },
  metadata: {
    description: "Acme agent plugins.",
    author: { name: "Acme" },
    license: "MIT",
  },
  targets: {
    antigravity: {
      outDir: "plugins/antigravity",
      plugins: {
        acme: { from: ["core"], components: ["skills", "commands"] },
      },
    },
    claude: {
      outDir: "plugins/claude",
      plugins: {
        acme: { from: ["core"], components: ["skills"] },
      },
    },
  },
});

Targets

Each target compiles the same source into one app's native plugin layout:

| Target | Native format | Output it writes | | ------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | cursor | Cursor plugin + marketplace | .cursor-plugin/marketplace.json; a .cursor-plugin/plugin.json per plugin | | claude | Claude plugin + marketplace | .claude-plugin/marketplace.json; a .claude-plugin/plugin.json per plugin | | antigravity | Antigravity CLI plugin | a plugin.json per plugin + optional mcp_config.json (no marketplace) | | copilot | GitHub Copilot plugins | .claude-plugin/marketplace.json mirrored to .github/plugin/marketplace.json; plugins under plugins/<name>/ | | codex | OpenAI Codex CLI plugins | .agents/plugins/marketplace.json; a .codex-plugin/plugin.json per plugin + optional .mcp.json |

Heads up: claude and copilot both write .claude-plugin/marketplace.json, so they need distinct outDirs (or separate repos). build errors on overlapping output paths.

New targets are added from official docs or real plugin examples — not guessed abstractions.

Source Plugins

The quick-start shape treats repo-level component directories as one source plugin. For more complex source content, keep source plugins under plugins/ and emit them into one or more target outputs:

plugins/
  core/
    plugin.pluginpack.json
    .mcp.json
    skills/
      release-notes/
        SKILL.md
    agents/
    commands/
    rules/
    hooks/
    assets/

A target can emit a source plugin directly, rename it, or merge multiple source plugins into one emitted plugin.

MCP Servers

A source plugin declares MCP servers with a standard .mcp.json file at its root ({ "mcpServers": { "name": { ... } } }), or with an mcpServers key in plugin.pluginpack.json. The file wins if both are present, and merging plugins with the same server name is an error.

Each target wires that MCP config into its native shape:

| Target | How MCP is wired | | ------------- | -------------------------------------------------------- | | claude | ships .mcp.json at the plugin root (auto-discovered) | | cursor | ships .mcp.json, referenced from plugin.json | | codex | ships .mcp.json, referenced from plugin.json | | copilot | ships .mcp.json, referenced from the marketplace entry | | antigravity | writes mcp_config.json beside plugin.json |

Update Check (claude, cursor)

Hosts don't reliably tell users a plugin is outdated (Claude Code's auto-update is off by default for third-party marketplaces; Cursor has no nudge at all). Opt in per target with updateCheck and pluginpack generates a session-start hook into each emitted plugin:

targets: {
  claude: { outDir: "...", updateCheck: {}, plugins: { ... } },
  cursor: { outDir: "...", updateCheck: {}, plugins: { ... } },
}

Each emitted plugin gains scripts/pluginpack-update-check.sh plus a hooks/hooks.json registration (merged into a source-authored hooks/hooks.json when one exists). At session start the script compares the version stamped at build time against the latest stable semver git tag of the plugin repo and, when behind, nudges: on claude via a user-visible systemMessage (with the /plugin update command to run); on cursor via agent context (Cursor's sessionStart hook has no user-visible output).

The check follows update-notifier discipline:

  • The repo URL comes from updateCheck.repository, defaulting to metadata.repository (an error if neither is set).
  • At most one git ls-remote per repo per 24h, cached under ${XDG_CACHE_HOME:-~/.cache}/pluginpack/ and shared across plugins from the same repo.
  • Fail-open: offline, missing git, odd tags, or any other problem exits silently.
  • Skipped entirely when CI is set or PLUGINPACK_NO_UPDATE_CHECK=1.

Disable for a single plugin with updateCheck: false on that plugin. Configuring updateCheck on copilot, antigravity, or codex is a config error — those hosts don't run plugin hooks.

Like MCP config, the generated hook is wired in regardless of a plugin's components selection: on cursor, the manifest's hooks field is set even if components doesn't include "hooks", since the check itself is a separate opt-in from which source-authored component dirs get emitted.

Install Snippet

Once a target's output is pushed to a repo, pluginpack install-info prints the real, doc-verified command or URL a user needs to add that marketplace — one per configured target:

pluginpack install-info
pluginpack install-info --target claude

The repo comes from targets.<name>.repository, defaulting to metadata.repository (an error if neither is set) — the same fallback updateCheck.repository uses. Every target today resolves to a real command except cursor, which has no CLI equivalent: it prints the repo URL and a note to paste it into Cursor's Dashboard under Team Marketplaces. See CONFORMANCE.md's "Install-snippet facts" section for the doc citation behind each target's snippet.

Target Overrides

Skill files are not always perfectly portable. When one app needs different frontmatter or content, add a target override next to the base file:

skills/release-notes/SKILL.md
skills/release-notes/targets/cursor/SKILL.md
skills/release-notes/targets/claude/SKILL.md

Resolution order is target override first, then the base file.

Other Shapes

There are two reasonable alternatives when the single-repo shape is not enough:

  • Single source repo, multiple output repos: best when each target ecosystem expects its own repo root shape.
  • Single source repo, release artifacts: best when users install zipped plugin payloads or release assets instead of browsing generated files in Git.

Why Not Just Copy Files?

For one target, copying files by hand may be enough. pluginpack starts to earn its keep when you need deterministic manifests, target-specific overrides, validation, and CI checks across multiple target repos.

CI Change Detection

pluginpack diff is designed for automation. It builds into a temporary directory, compares generated managed files against an existing plugin repo, and exits non-zero when the plugin repo is stale:

pluginpack diff --target cursor --against ../cursor-plugins
pluginpack diff --target claude --against ../claude-plugins

Use that in CI to fail clearly or to trigger an action that opens a PR against the generated plugin repo.

When a generated target repo intentionally owns a path, add ignoredDiffPaths to that target config. Entries are target-output-relative paths; a directory entry ignores everything below it.

To publish a repo-root file (for example a README authored once in the source repo) into a target's output, add rootFiles to that target config — a map of output path to source path (relative to the config root). Emitted root files are managed like any other generated file, so an output repo's README stays synced from source instead of hand-maintained per repo.

Configuration Reference

pluginpack.config.ts exports a config object (wrap it in defineConfig for types). src/schema.ts is the source of truth; this table enumerates every field. Paths marked safe relative reject absolute paths and .. escapes.

Top level

| Field | Type | Required | Meaning | | ---------- | ------ | -------- | -------------------------------------------------------------------------- | | name | string | yes | Marketplace/source name written into generated manifests. | | version | string | yes | Default version stamped into manifests (per-target/plugin overridable). | | source | object | no | Where source plugins come from (see source). | | metadata | object | no | Shared metadata merged into manifests (see metadata). | | targets | object | yes | Per-target output config, keyed by target name (see targets.<name>). |

source

| Field | Type | Required | Meaning | | ------------ | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | plugins | string | no | Directory to discover source plugins from. Defaults to plugins. | | skills | string | no | Repo-level skills directory; creates a root source plugin from sibling component dirs. | | rootPlugin | object | no | Metadata for that root skills plugin. Accepts all metadata fields plus id, name, description. id is the source-plugin name used in each target's from array. |

metadata (and source.rootPlugin)

| Field | Type | Meaning | | ------------- | ------------------------ | ------------------------------------ | | displayName | string | Human-readable name. | | description | string | Short description. | | author | { name, email?, url? } | Author identity (name required). | | owner | { name, email?, url? } | Marketplace owner (name required). | | homepage | string | Homepage URL. | | repository | string | Repository URL. | | license | string | SPDX license id. | | logo | string | Logo path or URL. | | keywords | string[] | Marketplace keywords. | | category | string | Marketplace category. | | tags | string[] | Free-form tags. |

targets.<name><name> is one of cursor, claude, antigravity, copilot, codex.

| Field | Type | Required | Meaning | | ------------------ | ---------------------- | -------- | ------------------------------------------------------------------------------------------- | | outDir | string | yes | Output directory for this target, relative to the config root. | | plugins | record | yes | Emitted plugins, keyed by emitted plugin name (see targets.<name>.plugins.<name>). | | marketplaceDir | string (safe relative) | no | Override the marketplace dir (defaults: .cursor-plugin / .claude-plugin). | | pluginRoot | string (safe relative) | no | Override the plugin root dir (claude; defaults to plugins). | | version | string | no | Override the version for this target (defaults to top-level version). | | manifest | object | no | Deep-merged into the generated marketplace manifest. | | ignoredDiffPaths | string[] | no | Output-relative paths diff ignores (a dir entry ignores everything below it). | | rootFiles | record (safe relative) | no | Map of output path → source path emitted verbatim at the output root. | | updateCheck | { repository? } | no | Generate a session-start update-check hook (claude/cursor only; see Update Check). | | repository | string | no | Repo this target's output lives in, for install-info (defaults to metadata.repository). |

targets.<name>.plugins.<name>

| Field | Type | Required | Meaning | | ------------- | ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | from | string[] (min 1) | yes | Source plugin ids to merge into this emitted plugin. | | path | string (safe relative) | no | Output path for the plugin, relative to outDir. Defaults to the plugin name (or pluginRoot/<name> for claude). | | version | string | no | Per-plugin version override. | | displayName | string | no | Per-plugin display name. | | description | string | no | Per-plugin description override. | | manifest | object | no | Deep-merged into the generated plugin manifest. | | entry | object | no | Deep-merged into the generated marketplace entry (the object in the marketplace plugins array). Use for target-specific entry fields pluginpack can't derive — e.g. Codex policy/category. | | components | string[] | no | Exact component set, overriding the target's smart default. | | updateCheck | false | no | Opt this plugin out of the target's update-check hook. |

Programmatic API

Everything the CLI does is exported from the package entry, so you can script builds (the pluginpack-action consumes these directly):

import {
  defineConfig,
  loadConfig,
  build,
  diffTarget,
  validateOutput,
  prune,
  clean,
  buildInstallSnippet,
  getSupportedInstallTargets,
  getUnsupportedInstallTargets,
} from "@gleanwork/pluginpack";

| Function | Returns | Purpose | | ------------------------------------- | --------------------------- | ----------------------------------------------------------------- | | defineConfig(config) | PluginpackConfig | Identity helper that types pluginpack.config.ts. | | loadConfig(cwd?, configPath?) | Promise<ResolvedProject> | Resolve config and discover source plugins. | | build(options?) | Promise<Artifact[]> | Emit configured targets; writes to disk unless options.dryRun. | | diffTarget(options) | Promise<DiffResult> | Build into a temp dir and compare against an existing repo. | | validateOutput(target, dir) | Promise<ValidationResult> | Validate an existing target output directory. | | prune(options?) | Promise<CleanupResult[]> | Remove stale managed files no longer emitted by the config. | | clean(options?) | Promise<CleanupResult[]> | Remove all managed files for configured targets. | | buildInstallSnippet(target, params) | InstallSnippet | The install command/URL for one target (see Install Snippet). | | getSupportedInstallTargets() | TargetName[] | Targets with a real install snippet today. | | getUnsupportedInstallTargets() | TargetName[] | Targets with none (empty today, kept for forward-compatibility). |

Option objects:

  • build{ cwd?, configPath?, target?, outDir?, dryRun? }
  • diffTarget{ cwd?, configPath?, target, against }
  • prune / clean{ cwd?, configPath?, target?, dryRun?, force? }

The result and config types (Artifact, DiffResult/DiffEntry, ValidationResult/ValidationIssue, CleanupResult/CleanupEntry, ResolvedProject, PluginpackConfig, TargetConfig, TargetName, …) are all exported for use in TypeScript.

CLI Reference

init

Create a starter pluginpack.config.ts and source plugin layout.

pluginpack init [options]

Examples:

  • pluginpack init

Exit codes:

  • 0 when files are created
  • 1 when files already exist or cannot be written

build

Compile configured source plugins into target-native plugin payloads.

pluginpack build [--target copilot|antigravity|cursor|claude|codex] [--out-dir <path>] [--dry-run]

Options:

  • --target <target>: Build only one configured target.
  • --out-dir <path>: Override the configured output directory for the selected target.
  • --dry-run: Resolve and print planned managed output paths without writing files.

Examples:

  • pluginpack build
  • pluginpack build --target cursor
  • pluginpack build --target claude --dry-run

Exit codes:

  • 0 when all selected targets build
  • 1 when config, source resolution, or file output fails

validate

Validate an existing target output directory for native manifest, path, and frontmatter requirements.

pluginpack validate --target copilot|antigravity|cursor|claude|codex [--dir <path>]

Options:

  • --target <target>: Required target validator.
  • --dir <path>: Directory to validate. Defaults to the configured target outDir.

Examples:

  • pluginpack validate --target cursor --dir ../cursor-plugins

Exit codes:

  • 0 when validation passes
  • 1 when validation finds errors

diff

Build into a temporary directory and compare generated managed files with an existing target repo.

pluginpack diff --target copilot|antigravity|cursor|claude|codex --against <path>

Options:

  • --target <target>: Required target to build and compare.
  • --against <path>: Existing target repo or output directory to compare against.

Examples:

  • pluginpack diff --target cursor --against ../cursor-plugins

Exit codes:

  • 0 when managed files match
  • 1 when managed files differ or the command fails

prune

Remove stale managed files that are no longer emitted by the current config.

pluginpack prune [--target copilot|antigravity|cursor|claude|codex] [--dry-run]

Options:

  • --target <target>: Prune only one configured target.
  • --dry-run: Print stale managed files without deleting them.
  • --force: Delete even paths that resolve inside the source tree or config.

Examples:

  • pluginpack prune
  • pluginpack prune --target claude --dry-run

Exit codes:

  • 0 when stale managed files are removed or listed
  • 1 when config, source resolution, or cleanup fails

clean

Remove all managed files for configured target outputs.

pluginpack clean [--target copilot|antigravity|cursor|claude|codex] [--dry-run]

Options:

  • --target <target>: Clean only one configured target.
  • --dry-run: Print managed files without deleting them.
  • --force: Delete even paths that resolve inside the source tree or config.

Examples:

  • pluginpack clean
  • pluginpack clean --target cursor --dry-run

Exit codes:

  • 0 when managed files are removed or listed
  • 1 when config, manifest loading, or cleanup fails

install-info

Print the real install command or URL for a target's built marketplace.

pluginpack install-info [--target copilot|antigravity|cursor|claude|codex] [--json]

Options:

  • --target <target>: Print only one configured target's install info.
  • --json: Print machine-readable JSON instead of text.

Examples:

  • pluginpack install-info
  • pluginpack install-info --target claude
  • pluginpack install-info --json

Exit codes:

  • 0 when install info is printed
  • 1 when config loading fails or a target has no repository configured

docs

Generate the README CLI reference section from command metadata.

pluginpack docs [options]

Options:

  • --check: Fail if README.md is not up to date.

Examples:

  • pluginpack docs
  • pluginpack docs --check

Exit codes:

  • 0 when docs are current or updated
  • 1 when --check finds stale docs