Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
441 changes: 441 additions & 0 deletions MIGRATING_TO_0.12.md

Large diffs are not rendered by default.

51 changes: 40 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,10 @@ Pluginpack emits packages in two ways:
`extensions["com.openai"]` automatically, so existing manifest config needs
no changes. MCP config is held to the stricter Agent Plugins rules (see
[MCP Directory](#mcp-directory)). Set `format: "legacy"` on the target to keep the older
`.codex-plugin/plugin.json` layout for Codex builds without Agent Plugins
support, which landed across v0.146 and v0.147 (see
`.codex-plugin/plugin.json` layout for Codex releases older than 0.147.0,
the first release that installs Agent Plugins packages. `format: "legacy"`
is deprecated in 0.12 and will be removed in 0.13; a build that uses it
prints a warning (see
[ADR 0001](./docs/adr/0001-codex-emits-agent-plugins-packages.md)).
- **`agent-plugins`** emits portable packages. On its own it has no client
profile, so it ships `skills`, `scripts`, `assets`, static files, and `mcp`.
Expand Down Expand Up @@ -411,14 +413,41 @@ This removes the old managed manifest and marketplace without deleting source
files. In the reverse direction, run `pluginpack clean --target agent-plugins`
before removing that target. The `agent-plugins` target does not ship Codex hooks.

**Upgrading from 0.11:** the `codex` target's output layout changes. Run
`pluginpack build`, and either publish the new layout or set
`format: "legacy"`. If `validate` reports skill names such as
`skill_name`, rename them before switching, because Agent Plugins clients
skip them. The build can also fail on MCP config that 0.11 copied as
authored, such as `cwd: "."` or a `${API_TOKEN}` placeholder. Change it to
follow the Agent Plugins rules in [MCP Directory](#mcp-directory), or set
`format: "legacy"` to keep writing `.mcp.json` as authored.
## Upgrading from 0.11

0.12 changes generated output for `codex`, `claude`, `cursor` and `copilot`.
Some configs and sources that 0.11 accepted now fail at config load, build or
`validate`. [`MIGRATING_TO_0.12.md`](./MIGRATING_TO_0.12.md) lists each error
with its fix, then the changed output, the new warnings and the API type
changes. The first 0.12 build rewrites each `.pluginpack/<target>.json` in a
format 0.11 can't read, so upgrade every machine that builds the same output
together.

- The `codex` target's output layout changes. Run `pluginpack build`, and
either publish the new layout or set `format: "legacy"` until 0.13. If
`validate` reports skill names such as `skill_name`, rename them before
switching, because Agent Plugins clients skip them. The build can also fail
on MCP config that 0.11 copied as authored, such as `cwd: "."` or a
`${API_TOKEN}` placeholder. Change it to follow the Agent Plugins rules in
[MCP Directory](#mcp-directory), or set `format: "legacy"` to keep writing
`.mcp.json` as authored.
- A `cursor` build fails when the shared MCP config uses
`${CLAUDE_PLUGIN_DATA}` or `${PLUGIN_DATA}`, which 0.11 shipped to Cursor
unexpanded:
`MCP server "local" uses ${CLAUDE_PLUGIN_DATA}, but Cursor has no plugin-data variable.`
The error stops every target, so nothing is written. Give `cursor` its own
`mcp/config.json` through `overrides`, or add `"mcp"` to its `exclude`.
- `claude`, `cursor` and `copilot` no longer copy MCP config as authored. A
`./bin/server` command becomes `${CLAUDE_PLUGIN_ROOT}/bin/server` for
`claude`, `${CURSOR_PLUGIN_ROOT}/bin/server` for `cursor` and
`${PLUGIN_ROOT}/bin/server` for `copilot`. A `claude` build fails on an MCP
server with `cwd`, because Claude Code ignores it. Other root and data
variables are renamed to each client's own. `"type": "streamable-http"`
becomes `"type": "http"` for `claude` and `copilot`, and `cursor` drops
`type` for stdio and HTTP servers.
- `validate` rejects `---json` frontmatter and tab-indented YAML, which 0.11
accepted. An unquoted number with a leading zero, such as `017`, now reads
as 17 instead of the octal 15.

## Legacy 0.10 Source Composition

Expand Down Expand Up @@ -737,7 +766,7 @@ Keys stripped by config parsing produce a warning that names the config file and
| Field | Type | Required | Meaning |
| ------------------ | ------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `outDir` | string | yes | Output directory for this target, relative to the config root. May be outside the project; it may not sit inside a source path. |
| `format` | `"agent-plugins"` \| `"legacy"` | no | `codex` only. `"legacy"` keeps the `.codex-plugin/plugin.json` layout; default `"agent-plugins"`. |
| `format` | `"agent-plugins"` \| `"legacy"` | no | `codex` only. `"legacy"` keeps the `.codex-plugin/plugin.json` layout; deprecated in 0.12, removed in 0.13. Default `"agent-plugins"`. |
| `plugins` | record | yes | Emitted plugins, keyed by emitted plugin name (see **`targets.<name>.plugins.<name>`**). |
| `marketplaceDir` | string (safe relative) | no | Compatibility field for `cursor` / `claude`: only the fixed host directory (`.cursor-plugin` / `.claude-plugin`) is accepted. Other targets do not use it. |
| `pluginRoot` | string (safe relative) | no | Override the plugin root dir (`claude`; defaults to `plugins`). |
Expand Down
12 changes: 9 additions & 3 deletions docs/adr/0001-codex-emits-agent-plugins-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ status: accepted
OpenAI's packaging docs make the Agent Plugins root `plugin.json` (OpenAI
settings under `extensions.com.openai`) the preferred format and keep
`.codex-plugin/plugin.json` only as a fallback. From 0.12.0 the `codex` target
therefore emits Agent Plugins packages by default. A `format: "legacy"` option
keeps the old layout for one release window, for users on Codex builds that
predate Agent Plugins support (it landed across v0.146 and v0.147).
therefore emits Agent Plugins packages by default.

A `format: "legacy"` option keeps the old layout for users on Codex releases
older than 0.147.0. Codex 0.146.0 reads an Agent Plugins `plugin.json`, but
0.147.0 is the first release that installs Agent Plugins packages and reads
their `mcp.json`
([release notes](https://github.com/openai/codex/releases/tag/rust-v0.147.0)).
`format: "legacy"` is deprecated in 0.12 and will be removed in 0.13. A build
that uses it prints a deprecation warning.

We deliberately do not emit both layouts in one build. A dual layout would need
two MCP files in different shapes (`mcp.json` and `.mcp.json`) and a
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"!dist/**/*.map",
"CHANGELOG.md",
"MIGRATING_TO_0.11.md",
"MIGRATING_TO_0.12.md",
"LICENSE",
"README.md"
],
Expand Down
1 change: 1 addition & 0 deletions scripts/verify-pack-contents.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const REQUIRED_FILES = [
"dist/index.d.ts",
"package.json",
"README.md",
"MIGRATING_TO_0.12.md",
"LICENSE",
];

Expand Down
3 changes: 2 additions & 1 deletion skills/authoring-pluginpack-config/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ so author it once. Don't add a per-target copy just to change
(root `plugin.json` + `mcp.json`). Codex-only manifest fields such as
`interface` move under `extensions["com.openai"]` automatically. Set
`format: "legacy"` on the codex target only to keep the old
`.codex-plugin/plugin.json` layout. The `agent-plugins` target emits
`.codex-plugin/plugin.json` layout for Codex releases older than 0.147.0.
`format: "legacy"` is deprecated in 0.12 and will be removed in 0.13. The `agent-plugins` target emits
portable packages. Without `clients` it writes no marketplace and can't include
`agents`, `commands`, `rules`, or `hooks`. With `clients: { codex: {}, copilot:
{} }` it builds each package once and writes each client's marketplace. Listing
Expand Down
4 changes: 3 additions & 1 deletion src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,9 @@ const targetSchema = z.object({
// external repo, which the README calls out.
outDir: z.string().min(1),
// codex only: "agent-plugins" (default) emits Agent Plugins packages;
// "legacy" emits the .codex-plugin layout. See docs/adr/0001.
// "legacy" emits the .codex-plugin layout for Codex releases older than
// 0.147.0. "legacy" is deprecated in 0.12 and will be removed in 0.13.
// See docs/adr/0001.
format: z.enum(["agent-plugins", "legacy"]).optional(),
marketplaceDir: safeRelativePath.optional(),
pluginRoot: safeRelativePath.optional(),
Expand Down
27 changes: 22 additions & 5 deletions src/targets/codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,13 @@ export const codexMarketplace = {
documentationUrl: "https://developers.openai.com/codex/plugins/build",
verifiedAt: "2026-10-02",
},
{
claim:
"Codex 0.146.0 reads Agent Plugins manifests (openai/codex#35105); 0.147.0 is the first release that installs Agent Plugins packages and parses their mcp.json (openai/codex#36544, #36796), so format: legacy serves releases older than 0.147.0",
documentationUrl:
"https://github.com/openai/codex/releases/tag/rust-v0.147.0",
verifiedAt: "2026-10-21",
},
{
claim:
"Codex rust-v0.146.0's legacy parser defaults every RawPluginManifest field; an omitted or blank name falls back to the plugin directory basename, and version/description/skills/hooks/mcpServers are optional",
Expand Down Expand Up @@ -376,9 +383,10 @@ const openaiProfile = { namespace: "com.openai" };
/**
* The pre-Agent Plugins Codex layout (`format: "legacy"`):
* `.codex-plugin/plugin.json` pointing at `skills/`, `hooks/`, and
* `.mcp.json`. Kept for Codex builds that predate Agent Plugins support
* (v0.146–v0.147) — see
* docs/adr/0001-codex-emits-agent-plugins-packages.md.
* `.mcp.json`. Kept for Codex releases older than 0.147.0, the first release
* that installs Agent Plugins packages and reads their `mcp.json` (0.146.0
* read the root `plugin.json` only). Deprecated in 0.12, removed in 0.13 —
* see docs/adr/0001-codex-emits-agent-plugins-packages.md.
*/
const codexLegacy: PluginTargetDefinition = {
...codexMarketplace,
Expand Down Expand Up @@ -421,6 +429,10 @@ const codexLegacy: PluginTargetDefinition = {
mcpDialect: "verbatim",
};

/** Printed once per build that uses `format: "legacy"`. */
const LEGACY_FORMAT_DEPRECATION =
'warning: targets.codex.format: "legacy" is deprecated and will be removed in pluginpack 0.13. It only serves Codex releases older than 0.147.0. Remove "format" from targets.codex to emit Agent Plugins packages.';

/**
* OpenAI Codex / ChatGPT target. Emits Agent Plugins packages by default —
* root `plugin.json` with OpenAI settings under `extensions.com.openai`, and
Expand All @@ -430,8 +442,13 @@ const codexLegacy: PluginTargetDefinition = {
export const codex: PluginTargetDefinition = {
...codexMarketplace,

forConfig: (targetConfig) =>
targetConfig.format === "legacy" ? codexLegacy : codex,
forConfig: (targetConfig) => {
if (targetConfig.format !== "legacy") {
return codex;
}
console.warn(LEGACY_FORMAT_DEPRECATION);
return codexLegacy;
},

buildPluginManifest: buildPackageManifest,
finalizeManifest: (manifest, pluginName) =>
Expand Down
9 changes: 9 additions & 0 deletions tests/conformance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ describe("emitted output conforms to external target schemas", () => {
it("codex emits the documented Codex plugin marketplace layout", async () => {
const result = await runBin("build", "--target", "codex");
expect(result.exitCode, String(result.stderr)).toBe(0);
expect(String(result.stderr)).not.toContain("deprecated");

const marketplace = readJson(
project.baseDir,
Expand Down Expand Up @@ -402,6 +403,14 @@ describe("emitted output conforms to external target schemas", () => {
});
const result = await runBin("build", "--target", "codex");
expect(result.exitCode, String(result.stderr)).toBe(0);
// Deprecated in 0.12, removed in 0.13: every build says so once, and
// names the config field and the way off it.
const deprecations = String(result.stderr)
.split("\n")
.filter((line) => line.includes('format: "legacy"'));
expect(deprecations).toEqual([
'warning: targets.codex.format: "legacy" is deprecated and will be removed in pluginpack 0.13. It only serves Codex releases older than 0.147.0. Remove "format" from targets.codex to emit Agent Plugins packages.',
]);

const plugin = readJson(
project.baseDir,
Expand Down
Loading