Skip to content

Latest commit

 

History

History
128 lines (112 loc) · 7.46 KB

CLAUDE.md

File metadata and controls

128 lines (112 loc) · 7.46 KB

pluginpack — agent guide

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.

Commands

  • npm run dev -- <args> — run the CLI from source (tsx src/cli.ts).
  • npm test — vitest. A pretest hook builds first, because the conformance tests run the real built binary (dist/cli.js) via bintastic.
  • 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's docs --check fails 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.

Architecture

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 use z.input, and parsed config uses z.infer. The build materializes these aliases as structural declarations without a Zod type dependency. Edit schemas here, not types.ts.
  • src/types.ts — non-config types; re-exports the config types from schema.ts.
  • src/components.ts — componentDirs + staticFiles (shared by render/config).
  • src/config.ts — loadConfig (jiti loads pluginpack.config.ts) and legacy 0.10 source-plugin discovery during the migration window.
  • src/source.ts — reads canonical direct shared/<plugin> sources, applies target overrides, packages mcp/, and retains the legacy filesystem source provider.
  • src/render.ts — legacy collectPluginFiles/resolveMcpServers composition.
  • src/partials.ts — loadPartials/resolvePartials: project-level {{> name}} text-reuse, wired into collectPluginFiles and withRootFiles. Substitution is real mustache rendering (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 checks mustache doesn't provide — circular partial reference detection at load time, and findUnsubstitutedPartialTags, the guard for tags in file types substitution skips (called from build() and, reading from disk, from validate).
  • 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 own PluginTargetDefinition (src/targets/types.ts).
  • src/targets/engine.ts — emitFromDefinition/validateFromDefinition: the one emit/validate engine every target runs through, driven by its PluginTargetDefinition. Also withRootFiles (injects per-target repo-root files into the artifact).
  • src/targets/validation-shared.ts — validators shared across targets whose shape actually matches (bare-string marketplace source, hooks.json shape, frontmatter conventions); a target with a genuinely different shape (e.g. Codex's structured source) 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).

Targets

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

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.

Shapes and gotchas

  • Recommended shape: shared/<plugin>/ canonical sources, overrides/<target>/<plugin>/ target overrides, and repositories/<target>/ generated-repository files.
  • Shared marketplace paths collide: claude and copilot both write .claude-plugin/marketplace.json. agent-plugins with clients.codex writes the codex target's .agents/plugins/marketplace.json, and with clients.copilot it writes .github/plugin/marketplace.json. Such targets need distinct outDirs. build() errors on overlapping output paths.
  • MCP: a canonical source keeps mcp/config.json, its implementation, tests, and mcp/pluginpack.json shipping-file map together. Target adapters translate the config into the native output. Legacy sources still read .mcp.json.

Conventions

  • Strict TypeScript, no any (the one exception is readJson in src/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