| 1 | # Diffinery plugins for Claude Code and Codex |
| 2 | |
| 3 | A plugin marketplace, kept in the Diffinery repository itself, that makes Claude Code and |
| 4 | OpenAI's Codex CLI work smoothly with repositories hosted on [Diffinery](https://diffinery.com), |
| 5 | the open-source git forge built for AI agents: on diffinery.com or on any instance you deploy |
| 6 | yourself (see [Any Diffinery instance](#any-diffinery-instance)). |
| 7 | It has two plugins: `diffinery` (Claude Code and Codex) and `diffinery-ci` (Claude Code), |
| 8 | described below. |
| 9 | |
| 10 | The plugin adds: |
| 11 | |
| 12 | | Piece | Claude Code | Codex | |
| 13 | | --- | --- | --- | |
| 14 | | `diffinery` MCP server (`<Diffinery URL>/mcp`, OAuth sign-in) | yes | yes (diffinery.com; see below for others) | |
| 15 | | `diffinery` skill: the CLI, git auth, the branch → PR → CI → merge loop, Diffinery's agent rules | yes | yes | |
| 16 | | `pr-status`: this branch's PR, its checks, and the log tail of failing jobs | `/diffinery:pr-status` command | `pr-status` skill | |
| 17 | | SessionStart hook: in a Diffinery clone, tells the agent the repo is on Diffinery and whether the `diffinery` CLI is installed and signed in | yes | yes, once you trust it (`/hooks`) | |
| 18 | |
| 19 | `diffinery-ci` is a Claude Code [mod](https://code.claude.com/docs/en/plugins/mods/overview) |
| 20 | that shows the branch's pull request and its CI checks live, and tells Claude when CI settles: |
| 21 | |
| 22 | | Piece | Where it works | |
| 23 | | --- | --- | |
| 24 | | Band above the prompt: PR number, title, checks (`✗ 1 failed · ● 3 running · ✓ 6`), review and merge state | Terminal, desktop app (Code tab), VS Code | |
| 25 | | `/diffinery-ci`: a pane with every check, its time and a link to its log, plus **Fix CI** and **Refresh**; `/diffinery-ci on` / `off` | Pane where Claude Code draws; a text answer everywhere else | |
| 26 | | Status line entry (`PR #7 ✓ 10/10`) and a toast when CI passes or fails | Where Claude Code draws | |
| 27 | | After the session pushes a branch or opens its PR, a note to Claude once that commit's checks settle (failures where the band shows the rest, passes too where nothing draws, so a cloud thread hears either way; `/diffinery-ci notes auto|failures|all|off` changes it, nothing to set up) | Everywhere hooks run, cloud sessions and project threads included | |
| 28 | |
| 29 | It reads the GraphQL API of the Diffinery instance in `DIFFINERY_URL`, the plugin's Diffinery |
| 30 | URL setting or the CLI's saved URL (in that order, else diffinery.com), with the token from |
| 31 | `DIFFINERY_TOKEN`, `diffinery login`, or gh signed in to the host (`diffinery auth setup-gh`), |
| 32 | and sends a token only to the instance it belongs to. It needs |
| 33 | Claude Code 2.1.287 or later (the desktop app: 2.1.286). Cloud sessions run its hooks but |
| 34 | draw no mod interface, so there it's the note to Claude and the `/diffinery-ci` text. |
| 35 | |
| 36 | Nothing here is needed just to use git: clone, fetch and push work with any git client |
| 37 | (`diffinery auth setup-git` or a token as the HTTPS password). The plugin is for pull |
| 38 | requests, CI and issues. |
| 39 | |
| 40 | ## Install |
| 41 | |
| 42 | codician/diffinery is public, so adding the marketplace needs no sign-in. The plugin works through |
| 43 | the `diffinery` CLI, so install it and sign in once per machine: |
| 44 | |
| 45 | ```sh |
| 46 | curl -fsSL https://diffinery.com/install | bash # Windows: irm https://diffinery.com/install.ps1 | iex |
| 47 | diffinery login |
| 48 | diffinery auth setup-git |
| 49 | ``` |
| 50 | |
| 51 | ### Claude Code |
| 52 | |
| 53 | ```sh |
| 54 | claude plugin marketplace add https://diffinery.com/codician/diffinery.git |
| 55 | claude plugin install diffinery@diffinery |
| 56 | ``` |
| 57 | |
| 58 | Or inside a session: `/plugin marketplace add https://diffinery.com/codician/diffinery.git`, |
| 59 | then `/plugin install diffinery@diffinery`, then `/reload-plugins` (or restart). |
| 60 | |
| 61 | Add `claude plugin install diffinery-ci@diffinery` for the live CI band. |
| 62 | |
| 63 | ### claude.ai, cloud sessions and projects |
| 64 | |
| 65 | claude.ai can't read this repository, so upload the plugins as files: download |
| 66 | https://diffinery.com/-/claude/diffinery.zip and https://diffinery.com/-/claude/diffinery-ci.zip, |
| 67 | then in claude.ai open **Customize → Plugins → Add → Upload plugin** for each. They reach your |
| 68 | cloud sessions at their next start. For a project's threads, also add them under |
| 69 | **Project settings → Plugins**. Build the same zips from a checkout with |
| 70 | `python3 scripts/pack-plugins.py` at the repository root (they land in `dist/`). |
| 71 | |
| 72 | ### Codex |
| 73 | |
| 74 | ```sh |
| 75 | codex plugin marketplace add https://diffinery.com/codician/diffinery.git |
| 76 | codex plugin add diffinery@diffinery |
| 77 | ``` |
| 78 | |
| 79 | Check with `codex plugin list` and `codex mcp list`. Codex runs plugin hooks only after |
| 80 | you review and trust them: open `/hooks` in the Codex TUI and trust the `diffinery` |
| 81 | SessionStart hook (optional; the skill works without it). |
| 82 | |
| 83 | ## Any Diffinery instance |
| 84 | |
| 85 | Diffinery is open source, and every plugin here works with any instance, not only |
| 86 | diffinery.com. Both Claude Code plugins have a **Diffinery URL** setting (`userConfig.url`, |
| 87 | default `https://diffinery.com`). It sets the MCP server's address (`.mcp.json` uses |
| 88 | `${user_config.url}/mcp`), the instance the SessionStart hook recognises in a clone (it gets |
| 89 | the value as `CLAUDE_PLUGIN_OPTION_URL`), and the instance `diffinery-ci` reads. |
| 90 | |
| 91 | - `diffinery setup --url https://git.example.com` sets it for you, in `pluginConfigs` in |
| 92 | `~/.claude/settings.json`, for the copies it installs (`diffinery@inline`) and for |
| 93 | marketplace installs alike. Install the CLI from the instance itself: |
| 94 | `curl -fsSL https://git.example.com/install | bash`. |
| 95 | - Claude Code asks for it when you enable a marketplace install; change it later in `/config`. |
| 96 | - The zips an instance serves at `/-/claude/<name>.zip` default the setting to that instance |
| 97 | (`apps/diffinery/src/docs/claude-plugins.ts`), so uploads to claude.ai need nothing else. |
| 98 | - The hook also knows `DIFFINERY_URL` and the URL `diffinery login` saved; `DIFFINERY_URL` |
| 99 | takes precedence for the hook and for `diffinery-ci`, as it does for the CLI. |
| 100 | |
| 101 | Codex substitutes no plugin settings, so the Codex manifest reads its own |
| 102 | `codex.mcp.json`, which names `https://diffinery.com/mcp`. A server of your own named |
| 103 | `diffinery` replaces it, and `diffinery setup --url` adds one when Codex is installed: |
| 104 | `codex mcp add diffinery --url https://git.example.com/mcp`. The skills and the hook work |
| 105 | with any instance in Codex too. |
| 106 | |
| 107 | ## Sign in |
| 108 | |
| 109 | The MCP server signs you in with OAuth in the browser: |
| 110 | |
| 111 | - Claude Code: run `/mcp`, pick `plugin:diffinery:diffinery`, choose Authenticate. |
| 112 | - Codex: `codex mcp login diffinery`. |
| 113 | |
| 114 | The skill also uses the `diffinery` CLI, which has its own sign-in: |
| 115 | |
| 116 | ```sh |
| 117 | curl -fsSL https://diffinery.com/install | bash # Windows: irm https://diffinery.com/install.ps1 | iex |
| 118 | diffinery login # or: diffinery login --device |
| 119 | diffinery auth setup-git # optional: git credential helper |
| 120 | ``` |
| 121 | |
| 122 | ## Headless use (CI, cloud sessions): `DIFFINERY_TOKEN` |
| 123 | |
| 124 | The `diffinery` CLI and `diffinery auth setup-git` pick up `DIFFINERY_TOKEN` on their own. |
| 125 | |
| 126 | The plugin's MCP server uses OAuth only. Neither client can take a token from the |
| 127 | environment when it is set and fall back to OAuth when it is not, from one config: |
| 128 | |
| 129 | - Claude Code turns OAuth off as soon as the server config has an `Authorization` |
| 130 | header, even an empty one, and plugin header helpers do not see variables like |
| 131 | `DIFFINERY_TOKEN`. |
| 132 | - Codex's `bearer_token_env_var` makes the server fail to start when the variable is unset. |
| 133 | |
| 134 | So on headless machines, add a token-based server of your own. It takes precedence over |
| 135 | the plugin's: |
| 136 | |
| 137 | ```sh |
| 138 | # Claude Code: same URL, so the plugin's OAuth entry is hidden while this one exists |
| 139 | claude mcp add --scope user --transport http diffinery https://diffinery.com/mcp \ |
| 140 | --header 'Authorization: Bearer ${DIFFINERY_TOKEN}' |
| 141 | |
| 142 | # Codex: same name, so it replaces the plugin's server |
| 143 | codex mcp add diffinery --url https://diffinery.com/mcp --bearer-token-env-var DIFFINERY_TOKEN |
| 144 | # ...or just for one run, without touching config.toml: |
| 145 | codex -c 'mcp_servers.diffinery.url="https://diffinery.com/mcp"' \ |
| 146 | -c 'mcp_servers.diffinery.bearer_token_env_var="DIFFINERY_TOKEN"' exec "..." |
| 147 | ``` |
| 148 | |
| 149 | Keep the single quotes in the Claude Code command: Claude Code expands |
| 150 | `${DIFFINERY_TOKEN}` when it connects, so the token is never written to its config. |
| 151 | |
| 152 | ## claude.ai and the Claude apps |
| 153 | |
| 154 | Settings → Connectors → Add custom connector, with the URL `https://diffinery.com/mcp` |
| 155 | (or your own instance's `https://git.example.com/mcp`). You sign in with your Diffinery |
| 156 | account when you connect it. |
| 157 | |
| 158 | ## gh |
| 159 | |
| 160 | Prefer the `diffinery` CLI: it knows Diffinery's extras (`--intent`, auto-merge, |
| 161 | `run logs`). `gh` works too once you run `diffinery auth setup-gh`, which signs gh in to |
| 162 | the Diffinery host (diffinery.com or yours) as a GitHub Enterprise host. That is also what lets Claude Code show the |
| 163 | branch's pull request with its CI status. See https://diffinery.com/docs/agents/github-cli. |
| 164 | |
| 165 | ## Layout |
| 166 | |
| 167 | Paths from the repository root: |
| 168 | |
| 169 | ``` |
| 170 | .claude-plugin/marketplace.json Claude Code marketplace |
| 171 | .agents/plugins/marketplace.json Codex marketplace |
| 172 | plugins/diffinery/ |
| 173 | .claude-plugin/plugin.json Claude Code manifest |
| 174 | .codex-plugin/plugin.json Codex manifest |
| 175 | .claude-plugin/icon.png the listing icon (also in diffinery-ci) |
| 176 | .mcp.json the diffinery MCP server (Claude Code: ${user_config.url}/mcp) |
| 177 | codex.mcp.json the diffinery MCP server (Codex: diffinery.com) |
| 178 | skills/diffinery/SKILL.md the skill (both clients) |
| 179 | commands/pr-status.md /pr-status (Claude Code) |
| 180 | codex-skills/pr-status/SKILL.md pr-status skill (Codex has no plugin commands) |
| 181 | hooks/hooks.json SessionStart hook (both clients) |
| 182 | hooks/session-start.sh the hook: POSIX sh, no network, silent outside Diffinery repos |
| 183 | plugins/diffinery-ci/ Claude Code only (a mod) |
| 184 | .claude-plugin/plugin.json manifest |
| 185 | hooks/hooks.json names the hooks module |
| 186 | hooks/register.tsx the mod: band, pane, status line, CI watch |
| 187 | hooks/lib.ts parsing and wording, no engine calls |
| 188 | types/index.d.ts the mod's state contract |
| 189 | tests/ci.test.tsx `claude plugin test plugins/diffinery-ci` |
| 190 | scripts/pack-plugins.py builds dist/<plugin>-<version>.zip for upload |
| 191 | apps/diffinery/claude-plugins/ the packed zips diffinery.com serves (see its README) |
| 192 | ``` |
| 193 | |
| 194 | Check changes with `claude plugin validate .` and `claude plugin validate plugins/diffinery` |
| 195 | from the repository root, and for the mod `claude plugin validate plugins/diffinery-ci` and |
| 196 | `claude plugin test plugins/diffinery-ci`. Bump `version` in both plugin manifests and both marketplace entries |
| 197 | together. |
| 198 | |
| 199 | More: https://diffinery.com/docs/connect |
| 200 | |
| 201 | ## License |
| 202 | |
| 203 | Apache-2.0, like the rest of the repository. See [LICENSE](../LICENSE). |