Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .agents/plugins/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "revdiff",
"interface": {
"displayName": "RevDiff"
},
"plugins": [
{
"name": "revdiff",
"source": {
"source": "local",
"path": "./plugins/codex"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Developer Tools"
},
{
"name": "revdiff-planning",
"source": {
"source": "local",
"path": "./plugins/revdiff-planning"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Developer Tools"
}
]
}
80 changes: 80 additions & 0 deletions .github/scripts/validate-codex-marketplace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env python3
"""Validate the Codex marketplace and its local plugin manifests."""

import json
from pathlib import Path


ROOT = Path(__file__).resolve().parents[2]
MARKETPLACE = ROOT / ".agents" / "plugins" / "marketplace.json"
EXPECTED_PLUGINS = {
"revdiff": {
"source": "./plugins/codex",
"files": (
"skills/revdiff/SKILL.md",
"skills/revdiff-plan/SKILL.md",
),
},
"revdiff-planning": {
"source": "./plugins/revdiff-planning",
"files": ("hooks/codex-hooks.json",),
},
}


def load_json(path: Path) -> dict:
with path.open(encoding="utf-8") as stream:
return json.load(stream)


def main() -> None:
if not __debug__:
raise RuntimeError("assertions must be enabled for marketplace validation")

marketplace = load_json(MARKETPLACE)
claude_marketplace = load_json(ROOT / ".claude-plugin" / "marketplace.json")
claude_versions = {
plugin["name"]: plugin["version"] for plugin in claude_marketplace["plugins"]
}
plugins = {plugin["name"]: plugin for plugin in marketplace["plugins"]}
assert set(plugins) == set(EXPECTED_PLUGINS), "unexpected Codex plugin set"

for name, expected in EXPECTED_PLUGINS.items():
plugin = plugins[name]
source = plugin["source"]
assert source["source"] == "local", f"{plugin['name']}: source must be local"
assert source["path"] == expected["source"], (
f"{name}: source is {source['path']}, expected {expected['source']}"
)

plugin_root = (ROOT / source["path"]).resolve()
assert plugin_root.is_relative_to(ROOT), f"{plugin['name']}: source escapes repo"

manifest_path = plugin_root / ".codex-plugin" / "plugin.json"
manifest = load_json(manifest_path)
assert manifest["name"] == plugin["name"], (
f"{plugin['name']}: manifest name is {manifest['name']}"
)
assert manifest["version"] == claude_versions[plugin["name"]], (
f"{plugin['name']}: Codex and Claude marketplace versions differ"
)

for field in ("skills", "hooks"):
if field not in manifest:
continue
component = (plugin_root / manifest[field]).resolve()
assert component.is_relative_to(plugin_root), (
f"{plugin['name']}: {field} path escapes plugin"
)
assert component.exists(), f"{plugin['name']}: missing {field} path"

for relative_path in expected["files"]:
assert (plugin_root / relative_path).is_file(), (
f"{plugin['name']}: missing packaged file {relative_path}"
)

print("Codex marketplace manifests are valid")


if __name__ == "__main__":
main()
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ jobs:
pip install pyyaml
python3 .github/scripts/validate-frontmatter.py

- name: validate Codex marketplace manifests
run: python3 .github/scripts/validate-codex-marketplace.py

- name: shellcheck
run: |
find . -name '*.sh' -not -path './.git/*' -not -path './vendor/*' -print0 | xargs -0 shellcheck
7 changes: 4 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ TUI for reviewing diffs, files, and documents with inline annotations, built wit
- Plugin lives at `.claude-plugin/` with `plugin.json`, `marketplace.json`, and `skills/`
- Skills path in `plugin.json` is relative to repo root, not to `.claude-plugin/`
- **CRITICAL: Version bumps happen at release only — never per-PR or per-change.** Do NOT prompt to bump `plugin.json` / `marketplace.json` after a plugin file change; the bump is done as part of the release process.
- When bumping at release, keep each marketplace entry synchronized with its plugin manifest. For `revdiff-planning`, update both `.claude-plugin/plugin.json` and `.codex-plugin/plugin.json` plus its version in `.claude-plugin/marketplace.json`.
- When bumping at release, keep every marketplace entry synchronized with its plugin manifests. For `revdiff`, update `.claude-plugin/plugin.json`, `plugins/codex/.codex-plugin/plugin.json`, and its version in `.claude-plugin/marketplace.json`. For `revdiff-planning`, update `plugins/revdiff-planning/.claude-plugin/plugin.json`, `plugins/revdiff-planning/.codex-plugin/plugin.json`, and its version in `.claude-plugin/marketplace.json`.
- **CRITICAL: Defer plugin version bumps when the change depends on a new binary feature.** If a plugin/launcher change relies on a `revdiff` binary feature, flag, env var, or exit code that is not yet in a tagged release, do NOT bump `plugin.json` / `marketplace.json` / `package.json` on the feature branch. The plugin (marketplace) and the binary (brew / `go install`) version independently — bumping the plugin early ships an updated launcher to users still running an old binary, causing a hard mismatch (e.g. the launcher passes an unknown flag, the old binary exits 1, every plugin-triggered review fails). Bump plugin/package versions as part of the binary version release, after the binary is tagged.
- Reference docs at `.claude-plugin/skills/revdiff/references/` — keep in sync with README.md:
- `install.md` — installation methods and plugin setup
Expand All @@ -78,9 +78,10 @@ TUI for reviewing diffs, files, and documents with inline annotations, built wit

## Codex Plugin and Skills
- Codex skills live at `plugins/codex/skills/` — two skills: `revdiff` (diff review) and `revdiff-plan` (plan review via last Codex assistant message)
- Manual skill install copies to `~/.codex/skills/<name>/`; automatic plan review is distributed separately through the `revdiff-planning` Codex plugin
- The `revdiff` Codex plugin packages both skills; automatic plan review is distributed separately through the `revdiff-planning` Codex plugin
- Codex marketplace metadata lives at `.agents/plugins/marketplace.json`; keep its local sources aligned with each `.codex-plugin/plugin.json`
- Keep Claude's default-discovered `PreToolUse/ExitPlanMode` config in `hooks/hooks.json`; the Codex manifest explicitly points its opt-in `Stop` hook at `hooks/codex-hooks.json`
- Script path resolution in SKILL.md falls back to `${CODEX_HOME:-$HOME/.codex}/skills/<skill>/scripts` when not running inside the revdiff repo
- Script path resolution in SKILL.md derives the installed plugin root from the skill's absolute catalogue path; marketplace installs live under Codex's plugin cache, not `~/.codex/skills/`
- Scripts are copies from `.claude-plugin/skills/revdiff/scripts/`, not symlinks — each has a source comment at top
- `detect-ref.sh` dispatches by VCS (`detect_git` / `detect_hg` / `detect_jj`) via `command -v` probes (jj → git → hg, matching `DetectVCS` precedence); git path stays byte-identical to the pre-refactor output. `read-latest-history.sh` uses the same VCS probe order for repo-root resolution.
- Codex automatic plan review runs only for `permission_mode=plan`, prefers a complete plan in `last_assistant_message`, and falls back whenever that field has no complete block to the last assistant message for the exact transcript/session/turn; manual `/revdiff-plan` remains the best-effort rollout fallback
Expand Down
19 changes: 6 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,28 +273,21 @@ revdiff ships with a [Codex CLI](https://github.com/openai/codex) plugin for int

The plugin uses the same terminal overlay mechanism (tmux, Zellij, herdr, kitty, wezterm, etc.) as the Claude Code plugin.

**Install the automatic plan-review plugin:**
**Install the diff-review skills and automatic plan-review plugin:**

```bash
codex plugin marketplace add umputun/revdiff
codex plugin add revdiff@revdiff
codex plugin add revdiff-planning@revdiff
```

If you previously copied the skills manually, remove `~/.codex/skills/revdiff` and `~/.codex/skills/revdiff-plan` after installing the plugin so the plugin copy is the only one in use.

Start a new session and trust the plugin hook through `/hooks`. The `Stop` hook runs only in Plan mode and first checks `last_assistant_message`; whenever that field has no complete `<proposed_plan>`, it reads the exact event transcript and selects the last assistant message for the matching `session_id` and `turn_id`, without depending on a provider-specific phase. A readable clarification turn is ignored. Missing or mismatched event data, dependencies, and launcher failures warn and fail open.

When annotations are present, the hook asks Codex to return the complete revised plan with a snapshot marker on the first line inside `<proposed_plan>`. The next round opens a rolling compare (`<new> <old>`); reviewed snapshots are replaced, and a clean review removes the final snapshot.

**Install the manual skills:**

```bash
# clone the repo first
git clone https://github.com/umputun/revdiff.git
cd revdiff

# copy skills to Codex skills directory
cp -r plugins/codex/skills/revdiff ~/.codex/skills/revdiff
cp -r plugins/codex/skills/revdiff-plan ~/.codex/skills/revdiff-plan
```
The `revdiff` plugin installs both interactive skills. The separate `revdiff-planning` plugin adds automatic Plan-mode review.

**Requirements:**

Expand All @@ -307,7 +300,7 @@ cp -r plugins/codex/skills/revdiff-plan ~/.codex/skills/revdiff-plan
- Codex treats exit code `10` as success-with-annotations and keeps captured output
- Automatic review uses the opt-in `revdiff-planning` plugin; `/revdiff-plan` remains a manual fallback
- Scripts are portable copies from the Claude Code plugin, not symlinks
- Plugin source lives under `plugins/codex/` in the repository
- Codex plugin source lives under `plugins/codex/` in the repository

### Integration with Other Tools

Expand Down
37 changes: 37 additions & 0 deletions plugins/codex/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "revdiff",
"version": "0.8.23",
"description": "Review diffs, files, and documents with inline annotations in a TUI overlay",
"author": {
"name": "Umputun",
"url": "https://github.com/umputun"
},
"homepage": "https://revdiff.com",
"repository": "https://github.com/umputun/revdiff",
"license": "MIT",
"keywords": [
"code-review",
"diff",
"annotations",
"tui"
],
"skills": "./skills/",
"interface": {
"displayName": "RevDiff",
"shortDescription": "Review diffs and plans with inline annotations",
"longDescription": "Review diffs, files, documents, and Codex responses in RevDiff, return line-level annotations to Codex, and repeat until the review is complete.",
"developerName": "Umputun",
"category": "Developer Tools",
"capabilities": [
"Interactive",
"Read",
"Write"
],
"websiteURL": "https://revdiff.com",
"defaultPrompt": [
"Review my current changes in RevDiff",
"Open this file in RevDiff",
"Review the last Codex response"
]
}
}
28 changes: 5 additions & 23 deletions plugins/codex/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,17 @@ This directory contains the **Codex CLI** skills for revdiff.

## Install

Install automatic Plan-mode review as an opt-in plugin:
Add the marketplace and install both plugins:

```bash
codex plugin marketplace add umputun/revdiff
codex plugin add revdiff@revdiff
codex plugin add revdiff-planning@revdiff
```

Start a new session and trust the hook through `/hooks`. It runs only in Plan mode and first checks the current Stop payload's `last_assistant_message`. If that field has no complete `<proposed_plan>`, the hook reads the exact event transcript and selects the last assistant message for the matching session and turn, regardless of provider-specific phase fields. Annotated revisions use rolling snapshot comparisons; `/revdiff-plan` below remains the manual fallback.
If you previously copied the skills manually, remove `~/.codex/skills/revdiff` and `~/.codex/skills/revdiff-plan` after installing the plugin so the plugin copy is the only one in use.

Install the manual skills by cloning the repository first:

```bash
git clone https://github.com/umputun/revdiff.git
cd revdiff
```

Then copy each skill to your Codex skills directory:

```bash
cp -r plugins/codex/skills/revdiff ~/.codex/skills/revdiff
cp -r plugins/codex/skills/revdiff-plan ~/.codex/skills/revdiff-plan
```

Alternatively, symlink them so `git pull` updates propagate without re-copying:

```bash
ln -s "$PWD/plugins/codex/skills/revdiff" ~/.codex/skills/revdiff
ln -s "$PWD/plugins/codex/skills/revdiff-plan" ~/.codex/skills/revdiff-plan
```
The `revdiff` plugin installs the manual `/revdiff` and `/revdiff-plan` skills. The separate `revdiff-planning` plugin adds automatic Plan-mode review. Start a new session and trust its hook through `/hooks`. It runs only in Plan mode and first checks the current Stop payload's `last_assistant_message`. If that field has no complete `<proposed_plan>`, the hook reads the exact event transcript and selects the last assistant message for the matching session and turn, regardless of provider-specific phase fields. Annotated revisions use rolling snapshot comparisons; `/revdiff-plan` remains the manual fallback.

## Skills

Expand Down Expand Up @@ -78,7 +60,7 @@ The skill reads `~/.codex/sessions/` rollout JSONL files, extracts the most rece

- Automatic review uses a Codex `Stop` hook; Claude Code uses `PreToolUse/ExitPlanMode`
- The automatic hook falls back whenever `last_assistant_message` lacks a complete plan, then uses the last assistant message for the exact transcript/session/turn; manual `/revdiff-plan` uses best-effort rollout discovery
- Script path resolution falls back to `$CODEX_HOME` (or `~/.codex`) instead of `$CLAUDE_SKILL_DIR`
- Script path resolution derives the installed plugin root from each skill's absolute catalogue path
- `AskUserQuestion` tool replaced with numbered-list prompts (Codex convention)
- `EnterPlanMode` replaced with inline markdown plan + confirmation prompt

Expand Down
16 changes: 4 additions & 12 deletions plugins/codex/skills/revdiff-plan/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
---
name: revdiff-plan
description: Review the last Codex assistant message (plan, analysis, or proposal) with inline annotations in a TUI overlay. Extracts the most recent response from Codex rollout files and opens it in revdiff for review and annotation. Activates on "revdiff-plan", "review plan with revdiff", "annotate plan", "review last response", "annotate codex output".
argument-hint: 'none'
allowed-tools: [Bash, Read, Edit, Write, Grep, Glob]
---

# revdiff-plan - Review Codex Output
Expand All @@ -11,25 +9,19 @@ Review the last Codex assistant message with inline annotations using revdiff TU

## Script Path Resolution

Resolve the script directory using repo root first, then fall back to Codex home:
Resolve `<plugin-root>` from this skill's absolute path in the available-skills catalogue. It is the directory containing this plugin's `.codex-plugin/plugin.json`. Then set:

```bash
SCRIPT_DIR="$(git rev-parse --show-toplevel 2>/dev/null)/plugins/codex/skills/revdiff-plan/scripts"
if [ ! -d "$SCRIPT_DIR" ]; then
SCRIPT_DIR="${CODEX_HOME:-$HOME/.codex}/skills/revdiff-plan/scripts"
fi
SCRIPT_DIR="<plugin-root>/skills/revdiff-plan/scripts"
```

Also resolve the launcher script from the revdiff skill:

```bash
LAUNCHER_DIR="$(git rev-parse --show-toplevel 2>/dev/null)/plugins/codex/skills/revdiff/scripts"
if [ ! -d "$LAUNCHER_DIR" ]; then
LAUNCHER_DIR="${CODEX_HOME:-$HOME/.codex}/skills/revdiff/scripts"
fi
LAUNCHER_DIR="<plugin-root>/skills/revdiff/scripts"
```

Use `$SCRIPT_DIR` and `$LAUNCHER_DIR` in place of script paths throughout this skill.
Replace `<plugin-root>` with the resolved absolute path before running either command. Use `$SCRIPT_DIR` and `$LAUNCHER_DIR` in place of script paths throughout this skill.

## Activation Triggers

Expand Down
15 changes: 5 additions & 10 deletions plugins/codex/skills/revdiff/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
---
name: revdiff
description: Review diffs, files, and documents with inline annotations in a TUI overlay, or answer questions about revdiff usage, configuration, themes, and keybindings. Opens revdiff in agterm/tmux/zellij/herdr/kitty/wezterm/cmux/ghostty/iterm2/emacs-vterm, captures annotations, and addresses them. Works in git, hg, and jj repos (auto-detected). Activates on "revdiff", "review diff", "review changes", "annotate diff", "git review with revdiff", "hg review with revdiff", "review jj change", "interactive diff review", "revdiff all files", "review all files", "browse all files", "revdiff <file>", "revdiff README.md", "revdiff /tmp/notes.txt", "review this file", "annotate this file", "review file with revdiff", "open this review in revdiff", "show review in revdiff", "review in revdiff", "revdiff config", "revdiff themes", "revdiff keybindings", "how to configure revdiff", "what themes does revdiff have".
argument-hint: 'optional: ref(s), "all files", or file path'
allowed-tools: [Bash, Read, Edit, Write, Grep, Glob]
description: Review diffs, files, and documents with inline annotations in a TUI overlay, or answer questions about revdiff usage, configuration, themes, and keybindings. Opens revdiff in agterm/tmux/zellij/herdr/kitty/wezterm/cmux/ghostty/iterm2/emacs-vterm, captures annotations, and addresses them. Works in git, hg, and jj repos (auto-detected). Activates on "revdiff", "review diff", "review changes", "annotate diff", "git review with revdiff", "hg review with revdiff", "review jj change", "interactive diff review", "revdiff all files", "review all files", "browse all files", "revdiff FILE", "revdiff README.md", "revdiff /tmp/notes.txt", "review this file", "annotate this file", "review file with revdiff", "open this review in revdiff", "show review in revdiff", "review in revdiff", "revdiff config", "revdiff themes", "revdiff keybindings", "how to configure revdiff", "what themes does revdiff have".
---

# revdiff - TUI Diff Review
Expand All @@ -11,18 +9,15 @@ Review diffs with inline annotations using revdiff TUI in a terminal overlay. Wo

## Script Path Resolution

Resolve the script directory using repo root first, then fall back to Codex home:
Resolve `<plugin-root>` from this skill's absolute path in the available-skills catalogue. It is the directory containing this plugin's `.codex-plugin/plugin.json`. Then set:

```bash
SCRIPT_DIR="$(git rev-parse --show-toplevel 2>/dev/null)/plugins/codex/skills/revdiff/scripts"
if [ ! -d "$SCRIPT_DIR" ]; then
SCRIPT_DIR="${CODEX_HOME:-$HOME/.codex}/skills/revdiff/scripts"
fi
SCRIPT_DIR="<plugin-root>/skills/revdiff/scripts"
```

Use `$SCRIPT_DIR` in place of script paths throughout this skill.
Replace `<plugin-root>` with the resolved absolute path before running the command. Use `$SCRIPT_DIR` in place of script paths throughout this skill.

**Note**: the launcher override chain (user via `${CLAUDE_PLUGIN_DATA}` → bundled) is Claude-only — codex users customize the launcher by editing `~/.codex/skills/revdiff/scripts/launch-revdiff.sh` directly.
**Note**: the launcher override chain (user via `${CLAUDE_PLUGIN_DATA}` → bundled) is Claude-only. Codex users can customize the launcher in a local marketplace checkout and reinstall the plugin.

## Activation Triggers

Expand Down
Loading