mirror of
https://github.com/openai/codex.git
synced 2026-05-16 01:02:48 +00:00
docs: frame plugin validation as general guidance
This commit is contained in:
@@ -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 <plugin-nam
|
||||
```
|
||||
|
||||
2. Edit `<plugin-path>/.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 \
|
||||
|
||||
`<parent-plugin-directory>` is the directory where the plugin folder `<plugin-name>` 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 <plugin-path>
|
||||
@@ -59,7 +59,7 @@ python3 .agents/skills/plugin-creator/scripts/validate_plugin.py <plugin-path>
|
||||
`~/.agents/plugins/marketplace.json`.
|
||||
- Creates plugin root at `/<parent-plugin-directory>/<plugin-name>/`.
|
||||
- Always creates `/<parent-plugin-directory>/<plugin-name>/.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.
|
||||
- `<plugin-name>` is normalized using skill-creator naming rules:
|
||||
@@ -149,9 +149,9 @@ python3 .agents/skills/plugin-creator/scripts/validate_plugin.py <plugin-path>
|
||||
|
||||
- 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 <path-to-skill-creator>/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 <plugin-path>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -172,10 +172,11 @@ personal marketplace unless the caller explicitly requests a repo-local destinat
|
||||
- Personal plugin: `~/.agents/plugins/marketplace.json`
|
||||
- Repo/team plugin: `<repo-root>/.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 <plugin-path>` 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 <plugin-path>` before handing back a generated plugin. It adds one
|
||||
intentional preflight check that rejects leftover `[TODO: ...]` placeholders.
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user