pluginpack compiles one source of portable agent plugins (skills, agents,
commands, rules, hooks, MCP servers, assets, metadata) into the native plugin
layouts each AI app expects. It is a build tool: it copies files, writes the
manifests each target needs, and validates the result. It is not a package
manager or publisher.
npm run dev -- <args>— run the CLI from source (tsx src/cli.ts).npm test— vitest. Apretesthook builds first, because the conformance tests run the real built binary (dist/cli.js) viabintastic.npm run check— the full gate (test:all):format:check→lint→typecheck→test→build→docs. Run this before considering work done.npm run audit—npm audit --omit=dev, also run in CI. Scoped to production dependencies on purpose: devDependencies (eslint, test fixtures, etc.) never ship in the published package, so a vulnerability there isn't a risk to consumers — don't add fixes/overrides for dev-only findings.npm run build— bundle with tsup.- After changing CLI commands/options, regenerate the README CLI reference with
node dist/cli.js docs(the gate'sdocs --checkfails if it is stale).
Node >= 22.12.0 (the engines floor in package.json, verified in CI against
that exact version), ESM, moduleResolution: nodenext, strict: true. Local
dev tooling (mise.toml) pins Node 24, but that's a dev-environment choice,
not the supported floor.
Data flows: config → discover source → collect/render files → emit per target
→ artifact (in-memory file map) → write / prune / validate / diff. The
Artifact (a Map<path, contents> plus managedPaths) is the seam — dry-run,
diff, prune, and validate all derive from it.
src/cli.ts— commander CLI:init,build,validate,diff,prune,clean,install-info,docs.src/schema.ts— zod schemas are the source of truth for config types; authoring types usez.input, and parsed config usesz.infer. The build materializes these aliases as structural declarations without a Zod type dependency. Edit schemas here, nottypes.ts.src/types.ts— non-config types; re-exports the config types fromschema.ts.src/components.ts—componentDirs+staticFiles(shared by render/config).src/config.ts—loadConfig(jiti loadspluginpack.config.ts) and legacy 0.10 source-plugin discovery during the migration window.src/source.ts— reads canonical directshared/<plugin>sources, applies target overrides, packagesmcp/, and retains the legacy filesystem source provider.src/render.ts— legacycollectPluginFiles/resolveMcpServerscomposition.src/partials.ts—loadPartials/resolvePartials: project-level{{> name}}text-reuse, wired intocollectPluginFilesandwithRootFiles. Substitution is realmustacherendering (view is always{}— no config/env data is ever exposed; this is not a general templating hook), but only ever sees partial tags: every other{{is swapped for a sentinel first and restored after, so text that merely looks like a template survives byte-for-byte. An unresolvable tag is a build error, not an empty string. Also holds two checksmustachedoesn't provide — circular partial reference detection at load time, andfindUnsubstitutedPartialTags, the guard for tags in file types substitution skips (called frombuild()and, reading from disk, fromvalidate).src/targets/registry.ts—targets: Record<TargetName, PluginTargetDefinition>, one file per target (src/targets/<name>.ts). Everything that varies by target — default components, manifest/marketplace builders, output paths, validation, install snippet — lives on that target's ownPluginTargetDefinition(src/targets/types.ts).src/targets/engine.ts—emitFromDefinition/validateFromDefinition: the one emit/validate engine every target runs through, driven by itsPluginTargetDefinition. AlsowithRootFiles(injects per-target repo-root files into the artifact).src/targets/validation-shared.ts— validators shared across targets whose shape actually matches (bare-string marketplacesource, hooks.json shape, frontmatter conventions); a target with a genuinely different shape (e.g. Codex's structuredsource) writes its own instead of forcing a fit.src/adapters.ts—emitTarget/validateOutput/targetNames, thin wrappers around the registry + engine.src/build.ts—build(): emit all targets →assertNoCrossTargetCollisions→ write/prune/manifest. Holds the delete guard.src/managed.ts— the managed-file manifest (.pluginpack/<target>.json),prune/clean, the delete guard, and path-safety checks.src/diff.ts—diffTarget: build to a temp dir and compare against an existing target repo (the CI staleness gate).
copilot, antigravity, cursor, claude, codex, agent-plugins. codex
and agent-plugins emit Agent Plugins packages through src/agent-plugins.ts
(the package module). The codex target owns its marketplace. With clients,
agent-plugins takes each client's marketplace, extension-directory placement
and validation from src/package-clients.ts. Each target's MCP config
is rendered by src/mcp.ts in the dialect its mcpDialect names. Adding a target means one
new file implementing PluginTargetDefinition (src/targets/<name>.ts) plus one
new entry in src/targets/registry.ts — TargetName (types.ts) is still a
separate union to extend, but everything else (CLI --target choices, build()'s
target set, emit/validate dispatch) derives from the registry automatically.
CONFORMANCE.md is the reference. Agent Plugins is the only format with a
referenceable upstream JSON Schema, so for the other targets the harness uses
the strongest available oracle per target: Cursor against vendored published schemas (tests/fixtures/cursor/,
provenance in SOURCE.md); Claude via claude plugin validate --strict (when
the CLI is present); Agent Plugins (and the Codex package) against the vendored 1.0 schemas
(tests/fixtures/agent-plugins/); Copilot and Antigravity structurally against their real
formats (github/copilot-plugins, Antigravity CLI plugin docs). Don't fetch
schemas at runtime — vendor a pinned copy with recorded provenance.
- Recommended shape:
shared/<plugin>/canonical sources,overrides/<target>/<plugin>/target overrides, andrepositories/<target>/generated-repository files. - Shared marketplace paths collide:
claudeandcopilotboth write.claude-plugin/marketplace.json.agent-pluginswithclients.codexwrites thecodextarget's.agents/plugins/marketplace.json, and withclients.copilotit writes.github/plugin/marketplace.json. Such targets need distinctoutDirs.build()errors on overlapping output paths. - MCP: a canonical source keeps
mcp/config.json, its implementation, tests, andmcp/pluginpack.jsonshipping-file map together. Target adapters translate the config into the native output. Legacy sources still read.mcp.json.
- Strict TypeScript, no
any(the one exception isreadJsoninsrc/targets/validation-shared.ts). - Prettier + eslint enforced by the gate.
- Conventional commits. Keep the README CLI reference regenerated.
Agent permissions, review rules and commit conventions: @AGENTS.md