feat(plugins): add codex plugin support#56
Conversation
Reviewer's GuideAdds Codex plugin support alongside existing Claude plugins by introducing Codex manifests, Codex marketplace metadata, wrapper workflow skills as Codex entry points, and expanding validation, docs, and CI to enforce parity and consistency across Claude and Codex plugin metadata and marketplace files. Entity relationship diagram for Claude/Codex manifests and marketplaceserDiagram
PLUGIN_DIR {
string name
string path
}
CLAUDE_MANIFEST {
string name
string version
string description
string author_name
}
CODEX_MANIFEST {
string name
string version
string description
string author_name
string skills
string hooks
string mcpServers
string apps
string interface_displayName
string interface_shortDescription
string interface_developerName
string interface_category
}
ROOT_MARKETPLACE {
string name
string plugins_name
string plugins_id
string plugins_description
string plugins_version
string plugins_type
string plugins_category
}
CLAUDE_MARKETPLACE {
string plugins_name
string plugins_source
}
CODEX_MARKETPLACE {
string interface_displayName
string plugins_name
string plugins_source_source
string plugins_source_path
string plugins_policy_installation
string plugins_policy_authentication
string plugins_category
}
PLUGIN_DIR ||--|| CLAUDE_MANIFEST : has_claude_manifest
PLUGIN_DIR ||--|| CODEX_MANIFEST : has_codex_manifest
PLUGIN_DIR ||--o{ ROOT_MARKETPLACE : listed_in_root
PLUGIN_DIR ||--o{ CLAUDE_MARKETPLACE : listed_in_claude
PLUGIN_DIR ||--o{ CODEX_MARKETPLACE : listed_in_codex
CLAUDE_MANIFEST ||--|| CODEX_MANIFEST : shared_metadata
ROOT_MARKETPLACE ||--o{ CLAUDE_MARKETPLACE : claude_subset
ROOT_MARKETPLACE ||--o{ CODEX_MARKETPLACE : codex_subset
Flow diagram for expanded plugin and marketplace validation scriptflowchart TD
start(["Start validate-plugins.sh"]) --> init
init["Set REPO_ROOT, FAILED, CHECKED, TMP_ERR
Install trap to clean TMP_ERR"] --> validateClaude
subgraph ClaudeManifestLoop["Loop over Claude manifests"]
direction TB
validateClaude["find **/.claude-plugin/plugin.json"] --> forClaude["For each Claude manifest"]
forClaude -->|increment CHECKED via check| callValidateClaude["validate_manifest(manifest, claude)"]
callValidateClaude --> claudeOK{Python validation passed?}
claudeOK -->|yes| logClaudeOK["Print ✅ Claude manifest: <plugin>"]
claudeOK -->|no| logClaudeErr["Print ❌ and stderr lines from TMP_ERR; increment FAILED"]
end
ClaudeManifestLoop --> CodexManifestLoop
subgraph CodexManifestLoop["Loop over Codex manifests"]
direction TB
CodexManifestLoopStart["find **/.codex-plugin/plugin.json"] --> forCodex["For each Codex manifest"]
forCodex -->|increment CHECKED via check| callValidateCodex["validate_manifest(manifest, codex)"]
callValidateCodex --> codexOK{Python validation passed?}
codexOK -->|yes| logCodexOK["Print ✅ Codex manifest: <plugin>"]
codexOK -->|no| logCodexErr["Print ❌ and stderr lines from TMP_ERR; increment FAILED"]
end
CodexManifestLoop --> crossParity
subgraph CrossChecks["Cross-file validations"]
direction TB
crossParity["check: Claude/Codex manifest parity"] --> parityOK{Python parity check passed?}
parityOK -->|yes| logParityOK["Print ✅ Claude/Codex manifest parity"]
parityOK -->|no| logParityErr["Print ❌ and errors; increment FAILED"]
logParityOK --> marketplace
logParityErr --> marketplace
marketplace["check: Marketplace files"] --> marketOK{Python marketplace checks passed?}
marketOK -->|yes| logMarketOK["Print ✅ Marketplace files"]
marketOK -->|no| logMarketErr["Print ❌ and errors; increment FAILED"]
end
CrossChecks --> summary
summary["Print 'Checked <CHECKED> item(s). <FAILED> failed.'"] --> decideExit{FAILED > 0?}
decideExit -->|yes| exit1[["Exit 1"]]
decideExit -->|no| exit0[["Exit 0"]]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 49 minutes and 25 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (34)
📝 WalkthroughWalkthroughThe pull request introduces dual-platform plugin support for both Claude and Codex by adding Codex plugin manifests, marketplace configuration, validation tooling, and corresponding documentation updates across the repository. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Hey - I've found 3 issues, and left some high level feedback:
- The new
validate_manifestfunction no longer enforces the previousskillsarray structure and path checks for Claude manifests, which removes validation of skill objects and their file existence; consider restoring these checks (or sharing them across modes) so Claude plugins keep the same level of validation as before. - Both
validate_cross_manifestandvalidate_marketplacesinfer the plugin set by walking the repo root and assuming every directory with.claude-plugin/plugin.jsonis a plugin and thatmarketplace.jsonmust list all of them; if you expect partial exposure or nested plugin directories in the future, you may want to decouple marketplace validation from this strict 1:1 directory mapping.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new `validate_manifest` function no longer enforces the previous `skills` array structure and path checks for Claude manifests, which removes validation of skill objects and their file existence; consider restoring these checks (or sharing them across modes) so Claude plugins keep the same level of validation as before.
- Both `validate_cross_manifest` and `validate_marketplaces` infer the plugin set by walking the repo root and assuming every directory with `.claude-plugin/plugin.json` is a plugin and that `marketplace.json` must list all of them; if you expect partial exposure or nested plugin directories in the future, you may want to decouple marketplace validation from this strict 1:1 directory mapping.
## Individual Comments
### Comment 1
<location path="scripts/validate-plugins.sh" line_range="167-168" />
<code_context>
+require(set(root_names) == plugin_set, "marketplace.json plugin names must match plugin directories")
+for plugin in root_marketplace.get("plugins", []):
+ name = plugin.get("name")
+ for field in ("name", "id", "description", "version", "type", "category"):
+ require(isinstance(plugin.get(field), str) and plugin[field].strip(), f"marketplace.json {name}: missing {field}")
+ require(plugin.get("id") == f"{name}@{root_marketplace.get('name')}", f"marketplace.json {name}: id does not match marketplace name")
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Avoid potential KeyError when a required marketplace field is missing.
Because `require` uses both `plugin.get(field)` and `plugin[field]`, a missing `field` will raise `KeyError` on `plugin[field]` before `require` can record the error. To let `require` handle missing fields gracefully, use only `.get`, e.g. `value = plugin.get(field)` followed by `require(isinstance(value, str) and value.strip(), ...)`, so missing fields are added to `errors` instead of causing a traceback.
</issue_to_address>
### Comment 2
<location path="scripts/validate-plugins.sh" line_range="200" />
<code_context>
+ if isinstance(policy, dict):
+ require(policy.get("installation") in {"NOT_AVAILABLE", "AVAILABLE", "INSTALLED_BY_DEFAULT"}, f".agents/plugins/marketplace.json {name}: invalid policy.installation")
+ require(policy.get("authentication") in {"ON_INSTALL", "ON_USE"}, f".agents/plugins/marketplace.json {name}: invalid policy.authentication")
+ require(isinstance(plugin.get("category"), str) and plugin["category"].strip(), f".agents/plugins/marketplace.json {name}: missing category")
+
+if errors:
</code_context>
<issue_to_address>
**issue (bug_risk):** Same KeyError risk for `category` in the Codex marketplace validation.
This mirrors the root marketplace issue: mixing `plugin.get("category")` with `plugin["category"]` will raise a `KeyError` when `category` is missing instead of flowing through `require` and aggregated `errors`. Please refactor to assign `category = plugin.get("category")` once and validate that variable.
</issue_to_address>
### Comment 3
<location path="scripts/validate-plugins.sh" line_range="154-155" />
<code_context>
+)
+plugin_set = set(plugin_dirs)
+
+def load_json(relpath):
+ with open(os.path.join(repo_root, relpath)) as f:
+ return json.load(f)
+
</code_context>
<issue_to_address>
**suggestion:** Uncaught FileNotFoundError when marketplace files are missing.
If any of `marketplace.json`, `.claude-plugin/marketplace.json`, or `.agents/plugins/marketplace.json` is missing, `load_json` will raise `FileNotFoundError` and terminate with a traceback instead of using the structured `- ...` error output. Consider wrapping the `open` call in `try/except` and appending a clear message to `errors` (e.g. `errors.append(f"missing {relpath}")`) to keep output consistent and avoid raw tracebacks in CI.
Suggested implementation:
```
plugin_set = set(plugin_dirs)
def load_json(relpath):
try:
with open(os.path.join(repo_root, relpath)) as f:
return json.load(f)
except FileNotFoundError:
errors.append(f"missing {relpath}")
return None
set -euo pipefail
```
These changes assume:
1. `errors` is a list defined at the top of the Python section (as shown) and later rendered as structured `- ...` output.
2. Callers of `load_json` should be updated (if necessary) to handle a possible `None` return value, e.g. by skipping further validation when the JSON is missing and relying on the appended error message instead.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Code Review
This pull request introduces comprehensive support for Codex by adding Codex-specific manifests and workflow skills for all plugins. It also updates the documentation and the validation script to ensure metadata parity between Claude and Codex. Feedback suggests updating the Codex marketplace name for clarity, correcting the plugin count badge in the README, and ensuring consistent validation logic for all marketplace files.
| "name": "duyet-claude-plugins", | ||
| "interface": { | ||
| "displayName": "Duyet Claude Plugins" | ||
| }, |
There was a problem hiding this comment.
The marketplace name and display name still refer to "Claude Plugins", but this file is specifically for the Codex marketplace (as documented in CLAUDE.md). It should be updated to reflect Codex to avoid confusion for users on that platform.
| "name": "duyet-claude-plugins", | |
| "interface": { | |
| "displayName": "Duyet Claude Plugins" | |
| }, | |
| "name": "duyet-codex-plugins", | |
| "interface": { | |
| "displayName": "Duyet Codex Plugins" | |
| }, |
| @@ -4,9 +4,9 @@ | |||
| [](LICENSE) | |||
| [](#plugins-at-a-glance) | |||
There was a problem hiding this comment.
The plugin count badge shows "13", but the new .agents/plugins/marketplace.json file contains 14 plugins. This should be updated to maintain consistency with the actual number of plugins in the marketplace.
| [](#plugins-at-a-glance) | |
| [](#plugins-at-a-glance) |
|
|
||
| claude_marketplace = load_json(".claude-plugin/marketplace.json") | ||
| claude_names = [plugin.get("name") for plugin in claude_marketplace.get("plugins", [])] | ||
| require(set(claude_names).issubset(plugin_set), ".claude-plugin/marketplace.json references unknown plugin") |
There was a problem hiding this comment.
There is an inconsistency in how marketplace files are validated. The root marketplace.json (line 164) and the Codex marketplace (line 183) require an exact match with the plugin directories, while the internal .claude-plugin/marketplace.json only requires it to be a subset. For consistency and to ensure all marketplaces are in sync, this should also use an exact match.
| require(set(claude_names).issubset(plugin_set), ".claude-plugin/marketplace.json references unknown plugin") | |
| require(set(claude_names) == plugin_set, ".claude-plugin/marketplace.json plugin names must match plugin directories") |
Add Codex plugin manifests beside the existing Claude plugin manifests for every plugin directory. Create a Codex marketplace and wrapper skills for Claude command/agent-heavy workflows. Expand validation and docs so Claude and Codex metadata stay in sync.
Suppress intentional ShellCheck warnings in existing helper scripts and fix the empty-file redirection. Make PR automation tolerate missing optional labels so labeling does not fail the workflow.
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@CLAUDE.md`:
- Around line 10-17: Move the Codex metadata paragraph currently inserted in the
Install section so it no longer splits the `/plugin install …` code block and
the "Alternative: `npx skills add …`" line; extract the four bullet lines about
Claude/Codex manifests and marketplaces into a new "Plugin Metadata" section (or
merge them into the existing "Plugin Structure" section referenced around lines
37–50) so both install commands remain contiguous and the metadata has its own
labeled place.
In `@frontend-design/.codex-plugin/plugin.json`:
- Around line 12-13: The manifest repeats the same text in description,
interface.shortDescription and interface.longDescription; update the
interface.longDescription in frontend-design/.codex-plugin/plugin.json (and
other manifests) to either omit it when identical or expand it into a more
detailed marketplace blurb listing key features, typical use cases, and unique
value props. Locate the JSON keys "description", "interface.shortDescription",
and "interface.longDescription" (and the top-level
"shortDescription"/"longDescription" if present) and either remove the duplicate
"interface.longDescription" field or replace its value with a longer, distinct
paragraph that highlights features (e.g., shadcn/ui integration, Recharts
support, example use cases, target audience) so the marketplace detail view is
informative.
In `@github/.codex-plugin/plugin.json`:
- Line 11: The displayName field in the plugin manifest uses incorrect casing
("Github"); update the JSON key "displayName" in
github/.codex-plugin/plugin.json (the "displayName" property) to use the correct
user-facing branding "GitHub" so the plugin shows the proper product name.
In `@scripts/validate-plugins.sh`:
- Around line 139-156: The validate_marketplaces check currently calls load_json
without handling file or JSON errors; update the nested load_json function
inside validate_marketplaces to catch exceptions from open/json.load
(FileNotFoundError, JSONDecodeError, OSError, etc.), and instead of letting the
exception escape, append a readable error message to the existing errors list
(include the relative path and the exception text) and return None so callers
can detect the failure; ensure all call sites that expect a dict handle a None
result and that the formatted error ends up in the same reporting flow as other
marketplace issues.
- Around line 109-119: The parity check currently builds plugin_dirs only from
directories containing a Claude manifest, so directories that only contain a
Codex manifest are skipped; change the logic to collect both sets (e.g., scan
repo_root for names where either os.path.isfile(os.path.join(repo_root, name,
".claude-plugin", "plugin.json")) or os.path.isfile(os.path.join(repo_root,
name, ".codex-plugin", "plugin.json"))), take the union, sort it into
plugin_dirs, and then iterate over plugin_dirs using the existing
claude_path/codex_path checks to append appropriate errors (missing
.claude-plugin/plugin.json or missing .codex-plugin/plugin.json) to errors.
- Around line 162-200: The marketplace validations collapse duplicate names by
using set(...) comparisons (root_names and codex_names) so duplicates slip
through; add explicit duplicate checks before the set equality checks: for
root_marketplace check that len(root_names) == len(set(root_names)) and raise a
require(...) error naming root_marketplace duplicates, then keep the existing
require(set(root_names) == plugin_set) check; do the same for codex_marketplace
using codex_names (len(codex_names) == len(set(codex_names))) and raise a clear
error if duplicates are found so duplicate plugin entries are detected.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: c35434af-7f7a-45a1-9782-cb35399193ed
📒 Files selected for processing (28)
.agents/plugins/marketplace.json.github/workflows/validate-plugins.ymlCLAUDE.mdCONTRIBUTING.mdREADME.mdclickhouse-monitoring/.codex-plugin/plugin.jsonclickhouse/.codex-plugin/plugin.jsoncommit/.codex-plugin/plugin.jsoncommit/skills/commit-workflow/SKILL.mddocs-generator/.codex-plugin/plugin.jsondocs-generator/skills/docs-generator-workflow/SKILL.mdduyetbot/.codex-plugin/plugin.jsonduyetbot/skills/duyetbot-workflow/SKILL.mdfix/.codex-plugin/plugin.jsonfix/skills/fix-workflow/SKILL.mdfrontend-design/.codex-plugin/plugin.jsongithub/.codex-plugin/plugin.jsongithub/skills/github-workflow/SKILL.mdinterview/.codex-plugin/plugin.jsoninterview/skills/interview-workflow/SKILL.mdorchestration/.codex-plugin/plugin.jsonprompt-engineering/.codex-plugin/plugin.jsonscripts/validate-plugins.shstatusline/.codex-plugin/plugin.jsonstatusline/skills/statusline-workflow/SKILL.mdteam-agents/.codex-plugin/plugin.jsonteam-agents/skills/team-agents-workflow/SKILL.mdunsloth-training/.codex-plugin/plugin.json
| "shortDescription": "Create distinctive, production-grade frontend interfaces avoiding AI slop aesthetics. Emphasizes shadcn/ui, Recharts, and bold design choices.", | ||
| "longDescription": "Create distinctive, production-grade frontend interfaces avoiding AI slop aesthetics. Emphasizes shadcn/ui, Recharts, and bold design choices.", |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Nit: longDescription duplicates shortDescription across all Codex manifests.
Every new Codex manifest in this PR (frontend-design, docs-generator, clickhouse-monitoring, fix, team-agents, plus the others not shown) copies the same string into description, interface.shortDescription, and interface.longDescription. longDescription is the marketplace detail view and benefits from elaboration (features, typical use cases). Consider differentiating it or omitting when identical. Non-blocking.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend-design/.codex-plugin/plugin.json` around lines 12 - 13, The manifest
repeats the same text in description, interface.shortDescription and
interface.longDescription; update the interface.longDescription in
frontend-design/.codex-plugin/plugin.json (and other manifests) to either omit
it when identical or expand it into a more detailed marketplace blurb listing
key features, typical use cases, and unique value props. Locate the JSON keys
"description", "interface.shortDescription", and "interface.longDescription"
(and the top-level "shortDescription"/"longDescription" if present) and either
remove the duplicate "interface.longDescription" field or replace its value with
a longer, distinct paragraph that highlights features (e.g., shadcn/ui
integration, Recharts support, example use cases, target audience) so the
marketplace detail view is informative.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b40d190d8e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| claude_marketplace = load_json(".claude-plugin/marketplace.json") | ||
| claude_names = [plugin.get("name") for plugin in claude_marketplace.get("plugins", [])] | ||
| require(set(claude_names).issubset(plugin_set), ".claude-plugin/marketplace.json references unknown plugin") |
There was a problem hiding this comment.
Require Claude marketplace to include every plugin
This check only verifies that .claude-plugin/marketplace.json does not reference unknown plugins, but it does not fail when a plugin directory is missing from that marketplace. In practice, adding a new plugin with a valid .claude-plugin/plugin.json can still pass CI while remaining undiscoverable to Claude marketplace users because its entry was omitted.
Useful? React with 👍 / 👎.
| if not isinstance(value, str) or not value.startswith("./"): | ||
| errors.append(f"'{field}' must be a relative string beginning with './'") | ||
| continue | ||
| target = os.path.normpath(os.path.join(plugin_dir, value)) | ||
| if not os.path.exists(target): |
There was a problem hiding this comment.
Block codex manifest paths from escaping plugin root
The path validation accepts any value starting with ./ and only checks os.path.exists after normpath, so values like ./../other-plugin/skills are treated as valid. That allows a plugin manifest to point outside its own directory, which can silently couple plugins together and break portability when a plugin is packaged or installed independently.
Useful? React with 👍 / 👎.
Restore Claude skill-array validation, harden marketplace error reporting, and avoid KeyError paths in validation. Refresh Codex marketplace display text and plugin count documentation.
aec74f8 to
478b756
Compare
Move Codex metadata docs out of the install block and normalize Codex manifest display metadata. Harden validation for Codex-only directories, missing marketplace files, and duplicate marketplace entries.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a5a9771d7d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| require(isinstance(path_value, str) and path_value.startswith("./"), f".agents/plugins/marketplace.json {name}: source.path must be relative string") | ||
| if isinstance(path_value, str): | ||
| require(os.path.isdir(os.path.join(repo_root, path_value)), f".agents/plugins/marketplace.json {name}: source.path does not exist") |
There was a problem hiding this comment.
Reject marketplace source paths that escape repository root
The Codex marketplace validation accepts any source.path that starts with ./ and exists after os.path.join, so paths like ./../some-dir will pass even though they resolve outside this repository. That allows a plugin entry to depend on arbitrary local directories, which can silently break portability and plugin isolation when the marketplace is used elsewhere. Normalize the joined path and assert it remains under repo_root before treating it as valid.
Useful? React with 👍 / 👎.
Summary
Test Plan
Summary by Sourcery
Add Codex plugin support alongside existing Claude plugins, including manifests, marketplace metadata, and validation tooling.
New Features:
Enhancements:
CI:
Documentation:
Summary by CodeRabbit
New Features
Documentation
Chores