-
Notifications
You must be signed in to change notification settings - Fork 0
feat: align Codex plugin with Claude OAuth and bundled CLI #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
ec1959f
feat: align Codex plugin with Claude OAuth and bundled CLI
ravern 796d873
style: use explicit loop in MCP tool check
ravern afdffb3
chore: update URLs after repository rename
ravern dfff4ce
docs: match Claude plugin public documentation
ravern 2cdb93e
chore: rename marketplace to greptile-plugin
ravern File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| name: CLI Check | ||
|
|
||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| paths: | ||
| - 'plugins/greptile/scripts/**' | ||
| - 'plugins/greptile/skills/**' | ||
| - '.github/workflows/cli-check.yml' | ||
| pull_request: | ||
| paths: | ||
| - 'plugins/greptile/scripts/**' | ||
| - 'plugins/greptile/skills/**' | ||
| - '.github/workflows/cli-check.yml' | ||
| schedule: | ||
| - cron: '41 8 * * *' | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| check: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 | ||
|
|
||
| - name: Recorded version is well formed | ||
| run: | | ||
| set -euo pipefail | ||
| version=$(tr -d '[:space:]' < plugins/greptile/scripts/greptile.version) | ||
| if ! printf '%s' "$version" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then | ||
| echo "::error::plugins/greptile/scripts/greptile.version is not a plain semver string." | ||
| exit 1 | ||
| fi | ||
| printf '%s' "$version" > "$RUNNER_TEMP/version.txt" | ||
| echo "Recorded version $version" | ||
|
|
||
| - name: Bundle runs and reports the recorded version | ||
| run: | | ||
| set -euo pipefail | ||
| version=$(cat "$RUNNER_TEMP/version.txt") | ||
| reported=$(node plugins/greptile/scripts/greptile.mjs --version | tr -d '[:space:]') | ||
| if [ "$reported" != "$version" ]; then | ||
| echo "::error::Vendored bundle reports $reported but greptile.version records $version." | ||
| exit 1 | ||
| fi | ||
| echo "Bundle reports $reported." | ||
|
|
||
| - name: Login and review run outside the checkout | ||
| run: | | ||
| set -euo pipefail | ||
| mkdir -p "$RUNNER_TEMP/installed plugin/scripts" | ||
| cp plugins/greptile/scripts/greptile.mjs "$RUNNER_TEMP/installed plugin/scripts/greptile.mjs" | ||
| cd "$RUNNER_TEMP" | ||
| GREPTILE_NO_UPDATE_CHECK=1 node "$RUNNER_TEMP/installed plugin/scripts/greptile.mjs" login --help | ||
| GREPTILE_NO_AUTO_INSTALL=1 GREPTILE_NO_UPDATE_CHECK=1 node "$RUNNER_TEMP/installed plugin/scripts/greptile.mjs" review --agent --help | ||
|
|
||
| - name: Bundle is byte-identical to the published npm release | ||
| run: | | ||
| set -euo pipefail | ||
| version=$(cat "$RUNNER_TEMP/version.txt") | ||
| cd "$RUNNER_TEMP" | ||
| npm pack "greptile@$version" >/dev/null | ||
| tar -xzf "greptile-$version.tgz" | ||
| cd "$GITHUB_WORKSPACE" | ||
| published=$(shasum -a 256 "$RUNNER_TEMP/package/dist/greptile.js" | cut -d' ' -f1) | ||
| vendored=$(shasum -a 256 plugins/greptile/scripts/greptile.mjs | cut -d' ' -f1) | ||
| if [ "$published" != "$vendored" ]; then | ||
| echo "::error::Vendored bundle does not match npm greptile@$version. published=$published vendored=$vendored. Re-vendor with: npm pack greptile@$version && tar -xzf greptile-$version.tgz && cp package/dist/greptile.js plugins/greptile/scripts/greptile.mjs" | ||
| exit 1 | ||
| fi | ||
| echo "Vendored bundle matches npm greptile@$version ($vendored)." | ||
|
|
||
| - name: Commands invoke the vendored bundle, not a fetched one | ||
| run: | | ||
| set -euo pipefail | ||
| for f in plugins/greptile/skills/review/SKILL.md plugins/greptile/skills/login/SKILL.md; do | ||
| block=$(awk '/^```/{fence = !fence; next} fence' "$f") | ||
| if [ -z "$block" ]; then | ||
| echo "::error::$f has no fenced command block to validate." | ||
| exit 1 | ||
| fi | ||
| if grep -q 'npx' <<<"$block"; then | ||
| echo "::error::$f still fetches the CLI with npx. The plugin vendors it; invoke <plugin-root>/scripts/greptile.mjs instead." | ||
| exit 1 | ||
| fi | ||
| if ! grep -qF 'node "<plugin-root>/scripts/greptile.mjs"' <<<"$block"; then | ||
| echo "::error::$f does not invoke the vendored bundle at <plugin-root>/scripts/greptile.mjs." | ||
| exit 1 | ||
| fi | ||
| if ! grep -qF 'GREPTILE_NO_UPDATE_CHECK=1 node "<plugin-root>/scripts/greptile.mjs"' <<<"$block"; then | ||
| echo "::error::$f invokes the vendored bundle without GREPTILE_NO_UPDATE_CHECK=1. CLI versions that predate plugin-install detection read this path as a standalone install and name an installer that cannot update the plugin's copy. --agent already suppresses the notice, so this is a second line of defence for invocations that drop it." | ||
| exit 1 | ||
| fi | ||
| done | ||
| echo "Commands invoke the vendored bundle with the update check disabled." | ||
|
|
||
| - name: Review command suppresses the renderer download | ||
| run: | | ||
| set -euo pipefail | ||
| block=$(awk '/^```/{fence = !fence; next} fence' plugins/greptile/skills/review/SKILL.md) | ||
| if ! grep -qF 'GREPTILE_NO_AUTO_INSTALL=1 GREPTILE_NO_UPDATE_CHECK=1 node "<plugin-root>/scripts/greptile.mjs"' <<<"$block"; then | ||
| echo "::error::plugins/greptile/skills/review/SKILL.md must invoke the bundle with GREPTILE_NO_AUTO_INSTALL=1 ahead of GREPTILE_NO_UPDATE_CHECK=1. README.md tells users the plugin never downloads the mmdr renderer; this is what makes that true." | ||
| exit 1 | ||
| fi | ||
| echo "Review command suppresses the renderer download." |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| name: MCP Check | ||
| on: | ||
| push: | ||
| branches: [main] | ||
| paths: | ||
| - 'plugins/**' | ||
| - '.github/workflows/mcp-check.yml' | ||
| pull_request: | ||
| paths: | ||
| - 'plugins/**' | ||
| - '.github/workflows/mcp-check.yml' | ||
| schedule: | ||
| - cron: '17 8 * * *' | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| check: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 | ||
|
|
||
| - name: Resolve the declared server config | ||
| run: | | ||
| set -euo pipefail | ||
| config=plugins/greptile/.mcp.json | ||
| jq -e 'if has("mcpServers") then .mcpServers else . end' "$config" > "$RUNNER_TEMP/servers.json" | ||
| if ! jq -e '.greptile | type == "object"' "$RUNNER_TEMP/servers.json" >/dev/null; then | ||
| echo "::error::$config does not declare a \"greptile\" server object in either the bare or mcpServers shape." | ||
| exit 1 | ||
| fi | ||
| if ! jq -e '.greptile.url | type == "string" and startswith("https://")' "$RUNNER_TEMP/servers.json" >/dev/null; then | ||
| echo "::error::$config does not declare an https url for the greptile server." | ||
| exit 1 | ||
| fi | ||
| jq -r '.greptile.url' "$RUNNER_TEMP/servers.json" > "$RUNNER_TEMP/url.txt" | ||
| echo "Resolved $(cat "$RUNNER_TEMP/url.txt")" | ||
|
|
||
| - name: Config uses OAuth discovery | ||
| run: | | ||
| set -euo pipefail | ||
| if jq -e '.greptile | has("headers") or has("bearer_token_env_var")' "$RUNNER_TEMP/servers.json" >/dev/null; then | ||
| echo "::error::MCP config must use OAuth discovery, without headers or bearer_token_env_var." | ||
| exit 1 | ||
| fi | ||
| echo "No headers declared." | ||
|
|
||
| - name: Server still advertises OAuth | ||
| run: | | ||
| set -euo pipefail | ||
| url=$(cat "$RUNNER_TEMP/url.txt") | ||
| response=$(curl -sS -o /dev/null -D - -X POST "$url" \ | ||
| -H 'Content-Type: application/json' \ | ||
| -H 'Accept: application/json, text/event-stream' \ | ||
| -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_code_reviews","arguments":{}}}' \ | ||
| -w 'http_code=%{http_code}\n') | ||
| echo "$response" | ||
| if ! grep -q 'http_code=401' <<<"$response"; then | ||
| echo "::error::An unauthenticated tools/call on $url did not return 401. Codex starts the OAuth flow from that challenge." | ||
| exit 1 | ||
| fi | ||
| if ! grep -qi '^www-authenticate:.*resource_metadata=' <<<"$response"; then | ||
| echo "::error::$url returned 401 without an RFC 9728 www-authenticate challenge. Codex needs resource_metadata to discover the authorization server." | ||
| exit 1 | ||
| fi | ||
| echo "OAuth challenge present." | ||
|
|
||
| - name: README documents exactly the tools the server serves | ||
| run: | | ||
| set -euo pipefail | ||
| url=$(cat "$RUNNER_TEMP/url.txt") | ||
| curl -sS -X POST "$url" \ | ||
| -H 'Content-Type: application/json' \ | ||
| -H 'Accept: application/json, text/event-stream' \ | ||
| -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' > "$RUNNER_TEMP/tools.json" | ||
| python3 - <<'PY' | ||
| import json, os, re, sys | ||
|
|
||
| with open(os.path.join(os.environ['RUNNER_TEMP'], 'tools.json')) as fh: | ||
| payload = json.load(fh) | ||
| if 'result' not in payload: | ||
| sys.exit(f"::error::tools/list returned no result: {json.dumps(payload)[:400]}") | ||
| served = {t['name'] for t in payload['result']['tools']} | ||
| if not served: | ||
| sys.exit('::error::tools/list returned no tools.') | ||
|
|
||
| readme = open('plugins/greptile/README.md').read() | ||
| documented = set(re.findall(r'^- `([a-z_]+)`', readme, re.M)) | ||
| documented |= set(re.findall(r'/ `([a-z_]+)`', readme)) | ||
|
|
||
| missing = sorted(served - documented) | ||
| extra = sorted(documented - served) | ||
| if missing: | ||
| print(f"::error::README does not document: {', '.join(missing)}") | ||
| if extra: | ||
| print(f"::error::README documents tools the server does not serve: {', '.join(extra)}") | ||
| if missing or extra: | ||
| sys.exit(1) | ||
| print(f"README documents all {len(served)} served tools.") | ||
| PY | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,41 +1,33 @@ | ||
| # Greptile Codex Plugin | ||
| # Greptile for Codex | ||
|
|
||
| Use Greptile reviews, MCP tools, and agent skills in OpenAI Codex. | ||
| The official [Greptile](https://greptile.com) plugin for Codex. | ||
|
|
||
| ## Install | ||
| This repository is a Codex plugin marketplace. Add it directly: | ||
|
|
||
| ```bash | ||
| ```sh | ||
| codex plugin marketplace add greptileai/greptile-codex-plugin | ||
| codex plugin add greptile@greptile-codex-plugins | ||
| ``` | ||
|
|
||
| Set your Greptile API key in the shell that launches Codex: | ||
| The plugin gives Codex two ways to work with Greptile: | ||
|
|
||
| ```bash | ||
| export GREPTILE_API_KEY="your-api-key" | ||
| ``` | ||
|
|
||
| Start a new Codex task after installation. | ||
|
|
||
| ## Included skills | ||
| - the **Greptile MCP server**, for reading review results and searching your | ||
| knowledge base and coding patterns | ||
| - the **Greptile CLI**, for reviewing your working branch before a pull request exists | ||
|
|
||
| - `check-pr`: inspect PR readiness and unresolved review feedback. | ||
| - `cli-review`: run a Greptile CLI review from a local checkout. | ||
| - `greploop`: fix feedback and re-review until the PR is clean. | ||
| Both authenticate over OAuth against your Greptile account, with separate | ||
| sign-ins for MCP and CLI. There is no API key to create and no separate CLI | ||
| installation: the CLI ships with the plugin and requires Node 22+. | ||
|
|
||
| The plugin also configures the public Greptile MCP endpoint at `https://api.greptile.com/mcp`. | ||
|
|
||
| ## Verify | ||
|
|
||
| ```bash | ||
| codex plugin marketplace list | ||
| codex plugin list | ||
| ``` | ||
| See [`plugins/greptile`](./plugins/greptile) for setup, workflows, and the full tool list. | ||
|
|
||
| The marketplace should appear as `greptile-codex-plugins`, with the `greptile` plugin installed. | ||
| ## Maintenance | ||
|
|
||
| ## Build provenance | ||
| Edit this repository directly. The plugin is no longer generated from another | ||
| repository. Keep the marketplace name `greptile-codex-plugins` and plugin name | ||
| `greptile` stable for existing installations. | ||
|
|
||
| - Skills source: https://github.com/greptileai/skills.git | ||
| - Branch: main | ||
| - Commit: 646e2dfad81e5157e97daecc802b68d3d2c4d1e4 | ||
| To update the CLI, copy `dist/greptile.js` from the published `greptile` npm | ||
| package to `plugins/greptile/scripts/greptile.mjs`, update `greptile.version`, | ||
| and bump the plugin version in `plugins/greptile/.codex-plugin/plugin.json`. | ||
| Open a PR and run CLI Check and MCP Check. Do not rebuild the npm artifact locally. |
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.