diff --git a/codex-rs/skills/src/assets/samples/plugin-creator/SKILL.md b/codex-rs/skills/src/assets/samples/plugin-creator/SKILL.md index b5ed0094b2..dde5779e6c 100644 --- a/codex-rs/skills/src/assets/samples/plugin-creator/SKILL.md +++ b/codex-rs/skills/src/assets/samples/plugin-creator/SKILL.md @@ -1,6 +1,6 @@ --- name: plugin-creator -description: Create and scaffold plugin directories for Codex with a required `.codex-plugin/plugin.json`, optional plugin folders/files, share-safe manifest defaults, and personal-marketplace entries by default. Use when Codex needs to create a new personal plugin, add optional plugin structure, or generate or update marketplace entries for plugin ordering and availability metadata. +description: Create and scaffold plugin directories for Codex with a required `.codex-plugin/plugin.json`, optional plugin folders/files, valid manifest defaults, and personal-marketplace entries by default. Use when Codex needs to create a new personal plugin, add optional plugin structure, or generate or update marketplace entries for plugin ordering and availability metadata. --- # Plugin Creator @@ -18,7 +18,7 @@ python3 .agents/skills/plugin-creator/scripts/create_basic_plugin.py /.codex-plugin/plugin.json` when the request gives specific metadata. - The scaffold starts with share-safe defaults and must not contain `[TODO: ...]` placeholders. + The scaffold starts with valid defaults and must not contain `[TODO: ...]` placeholders. 3. Generate or update the personal marketplace entry when the plugin should appear in Codex UI ordering: @@ -47,7 +47,7 @@ python3 .agents/skills/plugin-creator/scripts/create_basic_plugin.py my-plugin \ `` is the directory where the plugin folder `` will be created (for example `~/code/plugins`). -5. Before handing back a plugin intended for sharing, run: +5. Before handing back a generated plugin, run: ```bash python3 .agents/skills/plugin-creator/scripts/validate_plugin.py @@ -59,7 +59,7 @@ python3 .agents/skills/plugin-creator/scripts/validate_plugin.py `~/.agents/plugins/marketplace.json`. - Creates plugin root at `///`. - Always creates `///.codex-plugin/plugin.json`. -- Fills the manifest with the workspace-share schema shape that the upload service accepts. +- Fills the manifest with the validated schema shape that the ingestion path accepts. - Creates or updates `~/.agents/plugins/marketplace.json` when `--with-marketplace` is set. - If the marketplace file does not exist yet, seed a personal marketplace root before adding the first plugin entry. - `` is normalized using skill-creator naming rules: @@ -149,9 +149,9 @@ python3 .agents/skills/plugin-creator/scripts/validate_plugin.py - Outer folder name and `plugin.json` `"name"` are always the same normalized plugin name. - Do not remove required structure; keep `.codex-plugin/plugin.json` present. -- Do not leave `[TODO: ...]` placeholders in plugin manifests that may be shared. +- Do not leave `[TODO: ...]` placeholders in plugin manifests. - Keep `apps` and `mcpServers` out of `plugin.json` unless their companion files are actually created. -- Omit unsupported plugin manifest fields that workspace sharing rejects, including `hooks`. +- Omit unsupported plugin manifest fields that validation rejects, including `hooks`. - If creating files inside an existing plugin path, use `--force` only when overwrite is intentional. - Preserve any existing marketplace `interface.displayName`. - When generating marketplace entries, always write `policy.installation`, `policy.authentication`, and `category` even if their values are defaults. @@ -184,7 +184,7 @@ After editing `SKILL.md`, run: python3 /scripts/quick_validate.py .agents/skills/plugin-creator ``` -Before sharing a generated plugin, run: +Before handing back a generated plugin, run: ```bash python3 .agents/skills/plugin-creator/scripts/validate_plugin.py diff --git a/codex-rs/skills/src/assets/samples/plugin-creator/agents/openai.yaml b/codex-rs/skills/src/assets/samples/plugin-creator/agents/openai.yaml index 5086a2c8c6..19a9a6fc5b 100644 --- a/codex-rs/skills/src/assets/samples/plugin-creator/agents/openai.yaml +++ b/codex-rs/skills/src/assets/samples/plugin-creator/agents/openai.yaml @@ -1,6 +1,6 @@ interface: display_name: "Plugin Creator" short_description: "Scaffold plugins and marketplace entries" - default_prompt: "Use $plugin-creator to scaffold a share-safe plugin in the personal marketplace, then validate it before sharing." + default_prompt: "Use $plugin-creator to scaffold a valid plugin in the personal marketplace, then validate it before handing it back." icon_small: "./assets/plugin-creator-small.svg" icon_large: "./assets/plugin-creator.png" diff --git a/codex-rs/skills/src/assets/samples/plugin-creator/references/plugin-json-spec.md b/codex-rs/skills/src/assets/samples/plugin-creator/references/plugin-json-spec.md index c6920f3bb5..0fd444e64b 100644 --- a/codex-rs/skills/src/assets/samples/plugin-creator/references/plugin-json-spec.md +++ b/codex-rs/skills/src/assets/samples/plugin-creator/references/plugin-json-spec.md @@ -172,10 +172,11 @@ personal marketplace unless the caller explicitly requests a repo-local destinat - Personal plugin: `~/.agents/plugins/marketplace.json` - Repo/team plugin: `/.agents/plugins/marketplace.json` -### Workspace sharing notes +### Plugin validation notes -- The share upload path validates plugin manifests against the workspace plugin ingestion schema. -- Shareable plugin manifests must include real values for `name`, `version`, `description`, +- The validator mirrors the workspace plugin ingestion schema so generated plugins follow the same + manifest contract from the start. +- Plugin manifests must include real values for `name`, `version`, `description`, `author.name`, and the required `interface` fields. - `version` must use strict semver. - `websiteURL`, `privacyPolicyURL`, and `termsOfServiceURL` must be absolute `https://` URLs when @@ -184,7 +185,7 @@ personal marketplace unless the caller explicitly requests a repo-local destinat present. - `apps` and `mcpServers` should appear in `plugin.json` only when `.app.json` and `.mcp.json` actually exist. -- Workspace sharing rejects unsupported manifest fields such as `hooks`, so the scaffold keeps them - out of the shareable manifest. -- Run `scripts/validate_plugin.py ` before sharing. It mirrors the workspace ingestion - contract and adds one intentional preflight check that rejects leftover `[TODO: ...]` placeholders. +- Validation rejects unsupported manifest fields such as `hooks`, so the scaffold keeps them out of + generated manifests. +- Run `scripts/validate_plugin.py ` before handing back a generated plugin. It adds one + intentional preflight check that rejects leftover `[TODO: ...]` placeholders. diff --git a/codex-rs/skills/src/assets/samples/plugin-creator/scripts/create_basic_plugin.py b/codex-rs/skills/src/assets/samples/plugin-creator/scripts/create_basic_plugin.py index 1194eb9baa..fb882ce99a 100755 --- a/codex-rs/skills/src/assets/samples/plugin-creator/scripts/create_basic_plugin.py +++ b/codex-rs/skills/src/assets/samples/plugin-creator/scripts/create_basic_plugin.py @@ -172,7 +172,7 @@ def create_stub_file(path: Path, payload: dict, force: bool) -> None: def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( - description="Create a plugin skeleton with a share-safe plugin.json." + description="Create a plugin skeleton with a validation-ready plugin.json." ) parser.add_argument("plugin_name") parser.add_argument( diff --git a/codex-rs/skills/src/assets/samples/plugin-creator/scripts/validate_plugin.py b/codex-rs/skills/src/assets/samples/plugin-creator/scripts/validate_plugin.py index 42aabfe6cf..64518064e5 100644 --- a/codex-rs/skills/src/assets/samples/plugin-creator/scripts/validate_plugin.py +++ b/codex-rs/skills/src/assets/samples/plugin-creator/scripts/validate_plugin.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Validate a generated plugin against the workspace share ingestion contract.""" +"""Validate a generated plugin against the plugin ingestion contract.""" from __future__ import annotations @@ -26,9 +26,7 @@ HEX_COLOR_RE = re.compile(r"^#[0-9A-F]{6}$", re.IGNORECASE) def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser( - description="Validate a local plugin before sharing it from Codex." - ) + parser = argparse.ArgumentParser(description="Validate a local Codex plugin.") parser.add_argument("plugin_path", help="Path to the plugin root directory") return parser.parse_args() @@ -42,7 +40,7 @@ def main() -> None: for error in errors: print(f"- {error}") raise SystemExit(1) - print(f"Plugin is ready for workspace sharing: {plugin_root}") + print(f"Plugin validation passed: {plugin_root}") def validate_plugin(plugin_root: Path) -> list[str]: @@ -110,7 +108,7 @@ def validate_manifest_shape( "keywords", } for key in sorted(set(manifest) - allowed_keys): - errors.append(f"plugin.json field `{key}` is not accepted by workspace sharing") + errors.append(f"plugin.json field `{key}` is not accepted by plugin validation") validate_optional_non_empty_string(manifest, "id", errors) require_non_empty_string(manifest, "name", errors) @@ -256,7 +254,7 @@ def reject_unknown_fields( errors: list[str], ) -> None: for key in sorted(set(payload) - allowed_keys): - errors.append(f"plugin.json field `{prefix}.{key}` is not accepted by workspace sharing") + errors.append(f"plugin.json field `{prefix}.{key}` is not accepted by plugin validation") def validate_optional_https_url( @@ -357,7 +355,7 @@ def reject_companion_unknown_fields( errors: list[str], ) -> None: for key in sorted(set(payload) - allowed_keys): - errors.append(f"{prefix} field `{key}` is not accepted by workspace sharing") + errors.append(f"{prefix} field `{key}` is not accepted by plugin validation") def validate_skill_manifests(plugin_root: Path, errors: list[str]) -> None: @@ -542,7 +540,7 @@ def reject_skill_agent_unknown_fields( for key in sorted(set(payload) - allowed_keys): field = f"{prefix}.{key}" if prefix is not None else key errors.append( - f"skill `{skill_root.name}` agent field `{field}` is not accepted by workspace sharing" + f"skill `{skill_root.name}` agent field `{field}` is not accepted by plugin validation" )