feat: add support for Codex - #151
Draft
ryzizub wants to merge 18 commits into
Draft
Conversation
Wires the MCP servers, enforcement hooks, and reviewer agent into Codex, so the harness gets the whole plugin rather than only the skills it already reads from `.agents/skills/`. The hook scripts stay single-sourced. Codex passes `tool_name: "Bash"` with a plain string command and accepts the same `permissionDecision` JSON, and plain stdout from a SessionStart hook is injected the same way, so four of the six scripts port with no edits. Only the edit hooks differ: Codex's file-editing tool is `apply_patch` and it hands the hook a raw patch with no `file_path`, so `hook-payload-common.sh` reads both payload shapes and `analyze.sh` / `format.sh` stay identical across harnesses. `codex/install.sh` installs skills, MCP servers, hooks, and agents, merging into an existing `hooks.json` rather than overwriting it. `codex/loader_test.sh` asserts Codex actually loads all of it via `codex debug prompt-input`, which needs no credentials, and runs as a new `codex-loader` CI job. The reviewer agent's read-only contract is held by `sandbox_mode = "read-only"`, since Codex has no per-agent tool allowlist or agent-scoped PreToolUse hook. Verified against Codex CLI 0.153.4. Hooks there are a stable, default-on feature (`[features] hooks`) and do run on Windows, so the caveats in the issue no longer apply; the Windows limit is that these scripts need bash and jq. The Claude Code path is unchanged: `hooks/hooks.json` and `agents/flutter-reviewer.md` stay authoritative and `codex/` mirrors them. Closes #46 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two real problems the first CI run surfaced. `codex doctor` reports `mcp.config` as a warning, not ok, when the server executables are absent — the normal state on a runner with no Dart SDK. The check now treats a warning as acceptable and asserts the registration itself instead, so a warning can no longer hide a server pointing at the wrong command. `tomllib` needs Python 3.11, so the agent-file parsing died with a traceback on older interpreters (macOS system Python is 3.9). The parser is now resolved once in the preflight, falling back to tomli, with an actionable message when neither is available. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ryzizub
marked this pull request as draft
September 8, 2026 13:17
Codex has its own plugin system, so the installer was unnecessary. Adding
`.codex-plugin/plugin.json` and a marketplace entry in
`.agents/plugins/marketplace.json` makes this repo a Codex plugin, and the
install becomes the same two commands as Claude Code:
codex plugin marketplace add VeryGoodOpenSource/vgv-ai-flutter-plugin
codex plugin add vgv-ai-flutter-plugin@very-good-ventures
Codex then reads `skills/`, `.mcp.json`, and `hooks/hooks.json` from the very
files Claude Code uses, resolving `${CLAUDE_PLUGIN_ROOT}` as a compatibility
alias for the installed plugin directory. So `codex/hooks.json` and
`codex/config.toml` are gone — there is no second copy of the hooks and no MCP
block to keep in sync. The only change to the shared wiring is widening the
PostToolUse matcher to `apply_patch|Edit|Write`, which is inert on Claude Code.
A plugin cannot ship a Codex subagent: agents load only from `~/.codex/agents/`
or a project's `.codex/agents/`, and `agents` is neither a manifest field nor a
discovery path. `codex/agents/flutter-reviewer.toml` stays as a file users copy,
and it is all that is left in `codex/`.
release-please now bumps both manifests, and the loader test fails if their
versions drift. The loader test itself was rewritten to install the working tree
the way a user does and assert what Codex picked up: 35 assertions, up from 17.
Deletes codex/install.sh, codex/install_test.sh, codex/hooks.json,
codex/config.toml.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`codex plugin add` only takes PLUGIN@MARKETPLACE, and Codex silently drops any marketplace entry that is not a `local` source resolving inside the marketplace root. It does parse Claude Code's `.claude-plugin/marketplace.json`, but every entry in very-good-claude-code-marketplace uses `source: github`, so adding that marketplace to Codex yields "No marketplace plugins found" with no error. Writing this down so nobody tries to move the entry there and hits a silent dead end. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codex marketplace entries do support a remote source — `{"source": "url", "url":
"..."}` — which I had missed. So there is no need for a second marketplace: the
entry belongs in very-good-claude-code-marketplace next to the Claude Code one,
pointing back at this repo, and `.agents/plugins/marketplace.json` is deleted
from here.
Codex resolves `url` and `local` sources only. It silently drops `github`,
`git`, `git-subdir`, and any path outside the marketplace root — the marketplace
adds fine and `codex plugin list` reports "No marketplace plugins found" with no
error. That is why the existing `.claude-plugin/marketplace.json` cannot serve
Codex as-is, and why the two manifests coexist in the marketplace repo. Written
up in CONTRIBUTING.md with the exact entry to add.
Because a `url` source always resolves the default branch, the loader test now
synthesizes its own throwaway marketplace pointing at the working tree, so CI
still tests the checkout rather than main. It also validates every field Codex
ingestion requires in .codex-plugin/plugin.json, which nothing else checked once
the scaffold validator was out of the picture: 44 assertions, up from 35.
Requires the companion entry in very-good-claude-code-marketplace before the
documented install command works.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`.codex-plugin/plugin.json` is not required. Codex falls back to `.claude-plugin/plugin.json` for the plugin's name and version and discovers `.mcp.json` on its own, so an install without it is indistinguishable from one with it — same 15 skills, same `vgv-ai-flutter-plugin:<skill>` namespacing, same two MCP servers, same `codex plugin list` output. Verified both ways. What it would have added is Codex app presentation metadata (displayName, category, capabilities, defaultPrompt, icons), which is not worth a second manifest to keep version-synced. Removed from release-please's extra-files too. The consequence is recorded in CONTRIBUTING.md and AGENTS.md: `.claude-plugin/plugin.json` is now load-bearing for both harnesses rather than Claude Code alone. This repo now carries no Codex-specific plugin configuration at all. The only Codex-only files left are the reviewer agent TOML and the loader test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codex cannot bundle a subagent in a plugin, so the agent file is copied in. The README only showed the personal scope (~/.codex/agents/); the project scope (.codex/agents/ committed to the Flutter repo) is how most repos in the wild actually do it and gives a whole team the reviewer with no per-developer setup. Both are now documented, with links to the open upstream issues that would make the step unnecessary. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A JSON round-trip while adding and then removing the Codex manifest entry reflowed the file and escaped a non-ASCII character. Net change is zero, so restore it byte-for-byte. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tomllib only appears in codex/loader_test.sh, which cspell does not check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Verified this PR's assumptions against both hooks references. Three fixes. `analyze.sh` was described as blocking. It is a PostToolUse hook, and Claude Code's docs are explicit that exit 2 there does not block because the tool has already run — stderr is shown to the model instead. The effect (the model sees the analyzer output and fixes it) is what we wanted; the mechanism was described wrong. `block-cli-workarounds.sh` is PreToolUse, where exit 2 really does block, so those lines stand. The claim that a Codex plugin manifest rejects a `hooks` field was overstated: the scaffold validator refuses it, but the runtime docs say a manifest may override the hooks path. Moot here since we ship no manifest, so the bullet now just states that discovery defaults to <plugin root>/hooks/hooks.json. Recorded the matcher trap. Claude Code treats a matcher of only letters, digits, _, -, space, comma and | as exact tool names, and anything else as an unanchored regex. `apply_patch|Edit|Write` stays on the exact path, which is why it matches those three names and not MultiEdit. Adding a regex metacharacter would silently pull in MultiEdit and NotebookEdit, whose payloads hook-payload-common.sh does not read. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces hooks/scripts/hook-payload-common.sh with the same six-line jq expression in analyze.sh and format.sh. Codex hook payloads carry no file path and no changed-file list, so the paths still have to come out of the patch headers, but that no longer needs its own script. Simpler than the helper it replaces: selecting only Add/Update/Move headers means a delete contributes nothing, and a rename lists both paths so the existing-file check drops the stale one. No Move-to bookkeeping, no awk. Tests moved to dart-hooks_test.sh, which drives both hooks through a stub `dart` on PATH and asserts exactly which files reach the SDK, plus the exit codes. That covers more than the old helper-function tests did and still needs no Dart SDK in CI. Also verified against the real SDK by hand. Mutation testing while writing it found a genuine gap: removing the "*** Begin Patch" guard did not fail the suite, because the ^ anchor already rejects single-line shell commands. Added the cases that do catch it — a heredoc whose body starts with a patch marker, and a marker on a later line. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two scripts is a deliberate maintainability choice over merging them. Notes the two consequences so neither reads as a bug later: Claude Code runs hooks in a matcher group in parallel, so the pair race on the same file, and the payload jq is duplicated rather than shared. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Removes codex/loader_test.sh, hooks/scripts/dart-hooks_test.sh, and the codex-loader CI job, per review: no new test suites, only the two existing ones. Consequences, recorded in CONTRIBUTING.md rather than left implicit. CI no longer exercises Codex at all, so a change to the hooks or codex/ has to be verified by hand — the instructions for doing that replace the "run the loader test" step. And the payload reading in analyze.sh and format.sh ships without automated coverage, so the two jq copies have to be kept identical by review. Also drops the cspell entries for words that only existed in the removed documentation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Naming the two suites explicitly is fine now that this PR adds none, and the change was unrelated to Codex. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
MultiEdit was removed from Claude Code in v2.0.8 and is absent from the current tools documentation, so the matcher warning only needs to mention NotebookEdit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drops the Claude Code comparison list. The mechanics it described belong in CONTRIBUTING.md, which already covers them for contributors; the README section now just says how to install and how to add the reviewer agent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds .codex-plugin/plugin.json so this repo declares itself as a Codex plugin rather than relying on the fallback to .claude-plugin/plugin.json. It is also the only place Codex app presentation metadata can live (displayName, category, capabilities, defaultPrompt) and it points mcpServers at ./.mcp.json. release-please now bumps the version in both manifests via extra-files, so they cannot drift. The marketplace entry pointing here is handled in very-good-claude-code-marketplace. Verified: installs into a throwaway CODEX_HOME with 15/15 skills, both MCP servers registered, and codex doctor reporting config.load and mcp.config ok. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ryzizub
added a commit
that referenced
this pull request
Sep 9, 2026
Matches the shape of the Codex PR (#151): the harness-specific assets are a plain `gemini/` directory of files users copy, and everything else is docs. - `.gemini/` becomes `gemini/`. It was never config for this repo's own workspace; it is the MCP + hooks settings a user merges into their own `~/.gemini/settings.json` (or a project's `.gemini/settings.json` for a whole team), and the reviewer agent they copy into `agents/` beside it. The hook path drops the `$PWD` fallback and reads `${VGV_PLUGIN_ROOT}` plainly, since the plugin is never installed at the user's project root. - Drops the Gemini CLI loader CI job and `gemini-config_test.sh`. Nothing in CI exercises Codex either; both are verified by hand, and `CONTRIBUTING.md` says how. - The two hook suites keep their original cases and gain one assertion each for the Gemini response shape, rather than running the whole matrix twice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Marcos Sevilla <31174242+marcossevilla@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Wires up MCP, hooks, and the reviewer agent for Codex. Skills were already portable after #128. Closes #46.
Codex reads
skills/,.mcp.jsonandhooks/hooks.jsonfrom the same files Claude Code uses, and resolves${CLAUDE_PLUGIN_ROOT}as a compatibility alias, so almost nothing new is needed here.What changes:
hooks/hooks.json—PostToolUsematcher becomesapply_patch|Edit|Write. Codex names its edit toolapply_patch, so the hooks never fired there. Stays on Claude Code's exact-match path, soEditandWritebehave as before.analyze.sh/format.sh— Codex passes the raw patch intool_input.commandwith no file path anywhere, so both gain the samejqexpression to read either shape. Claude Code behavior unchanged..codex-plugin/plugin.json— Codex plugin manifest. release-please bumps its version alongside the Claude one.codex/agents/flutter-reviewer.toml— Codex cannot bundle a subagent in a plugin (openai/codex#18988), so users copy this file. Read-only is enforced bysandbox_moderather than the agent-scoped hook Claude Code uses — same guarantee, different mechanism, worth a look.README.md,CONTRIBUTING.md,CLAUDE.md,AGENTS.md.Two of the issue's caveats are stale on Codex 0.153.4: the hooks flag is
[features] hooks, on by default, and Codex does run hooks on Windows (our scripts need bash and jq, so WSL or Git Bash).The marketplace entry that points here is handled separately in
very-good-claude-code-marketplace.Verification
Tested against a real Codex 0.153.4 in a throwaway
CODEX_HOME. Installed as a plugin: 15/15 skills load, both MCP servers registered,codex doctorclean, hooks land at the discovery path.Live hook runs:
format.shformats on a realapply_patch;analyze.shexits 2 on one with a syntax error and the model reported the error back. The reviewer agent spawned, self-scoped viagit diff, emitted the contract table, and wrote nothing.Both hooks also checked by hand against the real Dart SDK. Existing suites pass (18, 25); cspell, markdownlint and
claude plugin validate .clean.Type of Change
feat)fix)refactor)docs)ci)chore)🤖 Generated with Claude Code